@guardbee/mcp-reverse-shield 0.1.1 → 0.2.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 (76) hide show
  1. package/README.md +39 -9
  2. package/TR.md +39 -9
  3. package/ZH.md +8 -3
  4. package/dist/__tests__/baseline.test.d.ts +2 -0
  5. package/dist/__tests__/baseline.test.d.ts.map +1 -0
  6. package/dist/__tests__/baseline.test.js +130 -0
  7. package/dist/__tests__/baseline.test.js.map +1 -0
  8. package/dist/__tests__/fixes.test.d.ts +2 -0
  9. package/dist/__tests__/fixes.test.d.ts.map +1 -0
  10. package/dist/__tests__/fixes.test.js +163 -0
  11. package/dist/__tests__/fixes.test.js.map +1 -0
  12. package/dist/__tests__/platforms.test.d.ts +2 -0
  13. package/dist/__tests__/platforms.test.d.ts.map +1 -0
  14. package/dist/__tests__/platforms.test.js +207 -0
  15. package/dist/__tests__/platforms.test.js.map +1 -0
  16. package/dist/__tests__/testFixtures.d.ts +16 -0
  17. package/dist/__tests__/testFixtures.d.ts.map +1 -1
  18. package/dist/__tests__/testFixtures.js +71 -0
  19. package/dist/__tests__/testFixtures.js.map +1 -1
  20. package/dist/apk.d.ts +1 -0
  21. package/dist/apk.d.ts.map +1 -1
  22. package/dist/apk.js +44 -10
  23. package/dist/apk.js.map +1 -1
  24. package/dist/baseline.d.ts +36 -0
  25. package/dist/baseline.d.ts.map +1 -0
  26. package/dist/baseline.js +87 -0
  27. package/dist/baseline.js.map +1 -0
  28. package/dist/cli.js +85 -37
  29. package/dist/cli.js.map +1 -1
  30. package/dist/config.d.ts +2 -0
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/config.js +6 -0
  33. package/dist/config.js.map +1 -1
  34. package/dist/fixes.d.ts +29 -0
  35. package/dist/fixes.d.ts.map +1 -0
  36. package/dist/fixes.js +396 -0
  37. package/dist/fixes.js.map +1 -0
  38. package/dist/frameworks.d.ts +19 -0
  39. package/dist/frameworks.d.ts.map +1 -0
  40. package/dist/frameworks.js +148 -0
  41. package/dist/frameworks.js.map +1 -0
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +2 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/ios.d.ts +24 -0
  47. package/dist/ios.d.ts.map +1 -0
  48. package/dist/ios.js +170 -0
  49. package/dist/ios.js.map +1 -0
  50. package/dist/native.d.ts.map +1 -1
  51. package/dist/native.js +3 -0
  52. package/dist/native.js.map +1 -1
  53. package/dist/plist.d.ts +7 -0
  54. package/dist/plist.d.ts.map +1 -0
  55. package/dist/plist.js +156 -0
  56. package/dist/plist.js.map +1 -0
  57. package/dist/protoxml.d.ts +14 -0
  58. package/dist/protoxml.d.ts.map +1 -0
  59. package/dist/protoxml.js +96 -0
  60. package/dist/protoxml.js.map +1 -0
  61. package/dist/rules.d.ts +1 -1
  62. package/dist/rules.d.ts.map +1 -1
  63. package/dist/rules.js +65 -0
  64. package/dist/rules.js.map +1 -1
  65. package/dist/sarif.d.ts +2 -1
  66. package/dist/sarif.d.ts.map +1 -1
  67. package/dist/sarif.js +4 -1
  68. package/dist/sarif.js.map +1 -1
  69. package/dist/scanner.d.ts.map +1 -1
  70. package/dist/scanner.js +47 -7
  71. package/dist/scanner.js.map +1 -1
  72. package/dist/server.d.ts +2 -1
  73. package/dist/server.d.ts.map +1 -1
  74. package/dist/server.js +28 -5
  75. package/dist/server.js.map +1 -1
  76. package/package.json +5 -2
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  An MCP server that tells you **how much your shipped app gives a reverse engineer for free**.
10
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.
11
+ Reverse-engineering MCPs now let an agent take an app it has no source for — a web bundle, an Electron app, an APK, an iOS app, a game, 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, Il2CppDumper, Hopper or the browser's devtools) would get without any effort, with the fix for each — and `suggest_fixes` turns those fixes into a diff against your build config.
12
12
 
13
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
14
 
@@ -17,7 +17,9 @@ Claude ──► reverse-shield ──► your build artifact / live site
17
17
  │
18
18
  ├─ web source maps (shipped, inline, publicly reachable), unminified / dev bundles, keys, internal hosts
19
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
20
+ ├─ android APK and AAB: debuggable, R8/ProGuard missing, signing scheme, backup, plain React Native bundle, unstripped .so
21
+ ├─ ios IPA and .app: Debug-configuration builds, development profiles, plain React Native bundle, the executable
22
+ ├─ engines Unity (IL2CPP metadata, Mono assemblies), PyInstaller, Java/Spring Boot JARs, Flutter
21
23
  ├─ native debug info, symbols, get-task-allow, hardened runtime, signing, PIE/NX/RELRO/canary (ELF, Mach-O, PE)
22
24
  └─ .NET obfuscation missing, embedded PDB, leaked build paths
23
25
  ```
@@ -44,6 +46,10 @@ Everything runs locally and offline (except `scan_url`), with its own parsers fo
44
46
  | `native_symbols_not_stripped` / `native_no_hardened_runtime` / `native_injection_entitlement` / `native_unsigned` | medium | native | Named functions / dylib injection / patched copies run |
45
47
  | `native_no_pie` / `native_no_nx` | medium | native | Exploit-friendly fixed addresses / executable stack |
46
48
  | `native_no_relro` / `native_no_stack_canary` / `native_adhoc_signed` / `native_build_path_leak` | low | native | Hardening gaps and build-host details |
49
+ | `ios_debug_build` / `ios_js_bundle_plaintext` | high | ios | Unoptimised code with full symbols and preview stubs / readable RN bundle |
50
+ | `ios_development_profile` | medium | ios | Debugger attach on registered devices |
51
+ | `unity_il2cpp_metadata` / `unity_mono_assemblies` | high | engines | Every class, method and string of the game / C# scripts via dnSpy |
52
+ | `python_pyinstaller_bundle` / `java_not_obfuscated` | high | engines | Decompilable `.pyc` files / Java classes with original names |
47
53
  | `dotnet_not_obfuscated` / `dotnet_embedded_pdb` | high | .NET | ILSpy/dnSpy output close to the original source |
48
54
 
49
55
  `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.
@@ -54,6 +60,10 @@ Everything runs locally and offline (except `scan_url`), with its own parsers fo
54
60
  - **.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
61
  - **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
62
  - **Repeats**: one key in 30 chunks is one finding (“also in 29 other files”), and long lists of unminified files collapse into one.
63
+ - **iOS Debug builds**: Xcode 16 puts a Debug build's code in `<App>.debug.dylib` next to `__preview.dylib`; either one in a bundle means the Debug configuration was shipped.
64
+ - **Unity**: IL2CPP's `global-metadata.dat` is flagged only when its standard header is intact (protected builds encrypt it); a Mono `Assembly-CSharp.dll` goes through the .NET name check.
65
+ - **PyInstaller**: the CArchive cookie is searched across the whole file (on macOS the archive sits in its own section before the code signature); PyArmor and Nuitka builds are noted, not flagged.
66
+ - **Flutter** apps are recognised, but Dart obfuscation is not measured — the note gives the build flags instead.
57
67
 
58
68
  ## Quick start
59
69
 
@@ -75,13 +85,16 @@ npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high
75
85
  npx @guardbee/mcp-reverse-shield scan ./app/build/outputs/apk/release/app-release.apk
76
86
  npx @guardbee/mcp-reverse-shield scan "/Applications/My App.app" --format=json
77
87
  npx @guardbee/mcp-reverse-shield scan https://app.example.com --format=sarif > reverse-shield.sarif
88
+ npx @guardbee/mcp-reverse-shield scan ./build/MyApp.ipa
89
+ npx @guardbee/mcp-reverse-shield suggest . --format=patch | git apply # review first: suggest . prints the same as text
78
90
  ```
79
91
 
80
92
  ## Tools
81
93
 
82
94
  | Tool | Purpose |
83
95
  |---|---|
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 |
96
+ | `scan_artifact` | Scan a file or directory: APK, AAB, IPA, iOS or macOS `.app`, Electron folder, `app.asar`, JAR, ELF/Mach-O/PE/.NET binary, JS bundle, source map, or a build output folder (Unity and Python builds included). Optional `ignore` and `baseline` |
97
+ | `suggest_fixes` | Read the project's build config (Vite, webpack, Next.js, Angular, CRA, Android Gradle and manifest, React Native, electron-builder / Forge, .NET project files, Flutter) and return the changes as unified diffs. Never writes files |
85
98
  | `scan_url` | Fetch a live page, check its same-site bundles, follow `sourceMappingURL` and probe `<bundle>.map` (third-party scripts are not fetched) |
86
99
  | `list_checks` | The check catalog with what each finding gives away and how to fix it |
87
100
 
@@ -93,25 +106,42 @@ Directories are walked including `dist/`, `build/` and `out/` (that is the point
93
106
  reverse-shield:
94
107
  fail-on: high
95
108
  max-files: 5000
109
+ baseline: .reverse-shield-baseline.json
96
110
  exclude:
97
111
  - "vendor/"
112
+ ignore: # accepted exposures: a rule id, or rule:path-substring
113
+ - bundle_secret:firebase-config.js
98
114
  ```
99
115
 
100
- ## CI
116
+ ## CI and baselines
117
+
118
+ Record what the current release exposes, then let CI fail only on what a change adds:
119
+
120
+ ```bash
121
+ npx @guardbee/mcp-reverse-shield scan ./dist --update-baseline # writes .reverse-shield-baseline.json
122
+ npx @guardbee/mcp-reverse-shield scan ./dist --baseline=.reverse-shield-baseline.json --fail-on=high
123
+ # Compared to baseline (grade C → D): 1 new, 2 fixed, 4 unchanged
124
+ ```
125
+
126
+ Findings are matched by check and location, with bundler content hashes (`index-BvL4J5f8.js`, `7321-dd7cb90a7de65945.js`) normalised, so a rebuild does not make old findings look new. SARIF results carry `baselineState` (`new` / `unchanged`).
127
+
128
+ The repository ships a composite action that scans, uploads SARIF, writes a job summary and exposes `grade` / `new-findings` outputs:
101
129
 
102
130
  ```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
131
+ - uses: GuardBee/guardbee-mcp/.github/actions/reverse-shield@main
105
132
  with:
106
- sarif_file: reverse-shield.sarif
133
+ target: dist
134
+ fail-on: high
135
+ baseline: .reverse-shield-baseline.json
107
136
  ```
108
137
 
109
138
  ## Limits
110
139
 
111
140
  - 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`.
141
+ - 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 it, then accept it with an `ignore` entry (`bundle_secret:firebase-config.js`).
113
142
  - 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.
143
+ - App Store IPAs are FairPlay-encrypted on distribution; the scanner reads the IPA you export (development, ad hoc or App Store export before Apple encrypts it), which is what a jailbroken device would also yield.
144
+ - `suggest_fixes` edits the common shapes of each config; unusual layouts (computed webpack configs, Gradle convention plugins) get no diff, and Forge and Flutter get instructions rather than a patch. Always review before applying.
115
145
 
116
146
  ## License
117
147
 
package/TR.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Yayınladığın uygulamanın bir tersine mühendise **neyi bedavaya verdiğini** söyleyen bir MCP server.
6
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.
7
+ Tersine mühendislik MCP'leri artık bir agent'ın kaynak kodu olmayan bir uygulamayı (web bundle, Electron uygulaması, APK, iOS uygulaması, oyun, 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, Il2CppDumper, Hopper veya tarayıcı devtools'u kullanan birinin) hiç uğraşmadan neler elde edeceğini her biri için düzeltmesiyle birlikte raporlar; `suggest_fixes` de bu düzeltmeleri build config'ine karşı bir diff'e çevirir.
8
8
 
9
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
10
 
@@ -13,7 +13,9 @@ Claude ──► reverse-shield ──► build artifact'ın / canlı site
13
13
  │
14
14
  ├─ web source map'ler (paketlenmiş, inline, herkese açık), minify edilmemiş / dev bundle, anahtarlar, iç host'lar
15
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
16
+ ├─ android APK ve AAB: debuggable, R8/ProGuard yok, imza şeması, backup, düz React Native bundle, strip edilmemiş .so
17
+ ├─ ios IPA ve .app: Debug yapılandırmasıyla derlenmiş build, development profili, düz React Native bundle, executable
18
+ ├─ motorlar Unity (IL2CPP metadata, Mono assembly'leri), PyInstaller, Java/Spring Boot JAR, Flutter
17
19
  ├─ native debug bilgisi, semboller, get-task-allow, hardened runtime, imza, PIE/NX/RELRO/canary (ELF, Mach-O, PE)
18
20
  └─ .NET obfuscation yok, gömülü PDB, sızan build yolları
19
21
  ```
@@ -40,6 +42,10 @@ Claude ──► reverse-shield ──► build artifact'ın / canlı site
40
42
  | `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
43
  | `native_no_pie` / `native_no_nx` | medium | native | Exploit'e elverişli sabit adresler / çalıştırılabilir stack |
42
44
  | `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ı |
45
+ | `ios_debug_build` / `ios_js_bundle_plaintext` | high | ios | Tam sembollü, optimize edilmemiş kod ve preview stub'ları / okunabilir RN bundle |
46
+ | `ios_development_profile` | medium | ios | Kayıtlı cihazlarda debugger bağlama |
47
+ | `unity_il2cpp_metadata` / `unity_mono_assemblies` | high | motorlar | Oyunun tüm sınıf, metot ve string'leri / dnSpy ile C# script'leri |
48
+ | `python_pyinstaller_bundle` / `java_not_obfuscated` | high | motorlar | Decompile edilebilir `.pyc` dosyaları / orijinal isimleriyle Java sınıfları |
43
49
  | `dotnet_not_obfuscated` / `dotnet_embedded_pdb` | high | .NET | Orijinal kaynağa çok yakın ILSpy/dnSpy çıktısı |
44
50
 
45
51
  `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.
@@ -50,6 +56,10 @@ Claude ──► reverse-shield ──► build artifact'ın / canlı site
50
56
  - **.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
57
  - **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
58
  - **Tekrarlar**: 30 chunk'taki aynı anahtar tek bulgudur ("also in 29 other files"); uzun minify edilmemiş dosya listeleri tek bulguda toplanır.
59
+ - **iOS Debug build'leri**: Xcode 16, Debug build'in kodunu `<App>.debug.dylib` içine koyar ve yanına `__preview.dylib` ekler; paketteki ikisinden biri Debug yapılandırmasının yayınlandığını gösterir.
60
+ - **Unity**: IL2CPP `global-metadata.dat` sadece standart başlığı bozulmamışsa işaretlenir (korumalı build'ler şifreler); Mono `Assembly-CSharp.dll` .NET isim kontrolünden geçer.
61
+ - **PyInstaller**: CArchive işaretçisi dosyanın tamamında aranır (macOS'te arşiv kod imzasından önce kendi bölümündedir); PyArmor ve Nuitka build'leri işaretlenmez, not düşülür.
62
+ - **Flutter** uygulamaları tanınır ama Dart obfuscation ölçülmez; not, bunun yerine build bayraklarını verir.
53
63
 
54
64
  ## Hızlı başlangıç
55
65
 
@@ -71,13 +81,16 @@ npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high
71
81
  npx @guardbee/mcp-reverse-shield scan ./app/build/outputs/apk/release/app-release.apk
72
82
  npx @guardbee/mcp-reverse-shield scan "/Applications/My App.app" --format=json
73
83
  npx @guardbee/mcp-reverse-shield scan https://app.example.com --format=sarif > reverse-shield.sarif
84
+ npx @guardbee/mcp-reverse-shield scan ./build/MyApp.ipa
85
+ npx @guardbee/mcp-reverse-shield suggest . --format=patch | git apply # önce gözden geçir: suggest . aynısını metin olarak basar
74
86
  ```
75
87
 
76
88
  ## Tool'lar
77
89
 
78
90
  | Tool | Amaç |
79
91
  |---|---|
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ü |
92
+ | `scan_artifact` | Bir dosya ya da dizini tarar: APK, AAB, IPA, iOS ya da macOS `.app`, Electron klasörü, `app.asar`, JAR, ELF/Mach-O/PE/.NET binary, JS bundle, source map ya da build çıktı klasörü (Unity ve Python build'leri dahil). İsteğe bağlı `ignore` ve `baseline` |
93
+ | `suggest_fixes` | Projenin build config'ini (Vite, webpack, Next.js, Angular, CRA, Android Gradle ve manifest, React Native, electron-builder / Forge, .NET proje dosyaları, Flutter) okur ve değişiklikleri unified diff olarak döndürür. Dosya yazmaz |
81
94
  | `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
95
  | `list_checks` | Her bulgunun neyi ele verdiği ve nasıl düzeltileceğiyle kontrol kataloğu |
83
96
 
@@ -89,25 +102,42 @@ Dizinler `dist/`, `build/` ve `out/` dahil yürünür (amaç zaten bu); `node_mo
89
102
  reverse-shield:
90
103
  fail-on: high
91
104
  max-files: 5000
105
+ baseline: .reverse-shield-baseline.json
92
106
  exclude:
93
107
  - "vendor/"
108
+ ignore: # kabul edilen bulgular: kural id'si ya da kural:yol-parçası
109
+ - bundle_secret:firebase-config.js
94
110
  ```
95
111
 
96
- ## CI
112
+ ## CI ve baseline
113
+
114
+ Mevcut sürümün neyi açık bıraktığını kaydet, sonra CI sadece bir değişikliğin eklediklerinde başarısız olsun:
115
+
116
+ ```bash
117
+ npx @guardbee/mcp-reverse-shield scan ./dist --update-baseline # .reverse-shield-baseline.json yazar
118
+ npx @guardbee/mcp-reverse-shield scan ./dist --baseline=.reverse-shield-baseline.json --fail-on=high
119
+ # Compared to baseline (grade C → D): 1 new, 2 fixed, 4 unchanged
120
+ ```
121
+
122
+ Bulgular kontrol ve konuma göre eşleştirilir; bundler hash'leri (`index-BvL4J5f8.js`, `7321-dd7cb90a7de65945.js`) normalize edildiği için yeniden build almak eski bulguları yeni göstermez. SARIF sonuçları `baselineState` (`new` / `unchanged`) taşır.
123
+
124
+ Repoda tarayan, SARIF yükleyen, job özeti yazan ve `grade` / `new-findings` çıktıları veren bir composite action var:
97
125
 
98
126
  ```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
127
+ - uses: GuardBee/guardbee-mcp/.github/actions/reverse-shield@main
101
128
  with:
102
- sarif_file: reverse-shield.sarif
129
+ target: dist
130
+ fail-on: high
131
+ baseline: .reverse-shield-baseline.json
103
132
  ```
104
133
 
105
134
  ## Sınırlar
106
135
 
107
136
  - 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.
137
+ - 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 bir `ignore` girdisiyle kabul et (`bundle_secret:firebase-config.js`).
109
138
  - 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.
139
+ - App Store IPA'ları dağıtımda FairPlay ile şifrelenir; tarayıcı senin dışa aktardığın IPA'yı okur (development, ad hoc ya da Apple şifrelemeden önceki App Store export'u), ki jailbreak'li bir cihazdan da aynısı elde edilir.
140
+ - `suggest_fixes` her config'in yaygın biçimlerini düzenler; alışılmadık yapılar (hesaplanan webpack config'leri, Gradle convention plugin'leri) diff almaz, Forge ve Flutter için patch yerine talimat verilir. Uygulamadan önce mutlaka gözden geçir.
111
141
 
112
142
  ## Lisans
113
143
 
package/ZH.md CHANGED
@@ -13,7 +13,9 @@ Claude ──► reverse-shield ──► 你的构建产物 / 线上站点
13
13
  │
14
14
  ├─ web source map(随包发布、内联、可公开下载)、未压缩 / 开发版打包、密钥、内部主机
15
15
  ├─ electron fuse(RunAsNode、--inspect、NODE_OPTIONS、ASAR 完整性)、未打包的应用代码、app.asar 内的代码
16
- ├─ android debuggable、未启用 R8/ProGuard、签名方案、备份、明文 React Native 包、未剥离符号的 .so
16
+ ├─ android APK 与 AAB:debuggable、未启用 R8/ProGuard、签名方案、备份、明文 React Native 包、未剥离符号的 .so
17
+ ├─ ios IPA 与 .app:以 Debug 配置构建、开发描述文件、明文 React Native 包、可执行文件
18
+ ├─ 引擎 Unity(IL2CPP 元数据、Mono 程序集)、PyInstaller、Java/Spring Boot JAR、Flutter
17
19
  ├─ native 调试信息、符号、get-task-allow、hardened runtime、签名、PIE/NX/RELRO/canary(ELF、Mach-O、PE)
18
20
  └─ .NET 未混淆、内嵌 PDB、泄露的构建路径
19
21
  ```
@@ -24,7 +26,8 @@ Claude ──► reverse-shield ──► 你的构建产物 / 线上站点
24
26
 
25
27
  | 工具 | 用途 |
26
28
  |---|---|
27
- | `scan_artifact` | 扫描文件或目录:APK、`.app`、Electron 目录、`app.asar`、ELF/Mach-O/PE/.NET 二进制、JS 打包文件、source map 或构建输出目录 |
29
+ | `scan_artifact` | 扫描文件或目录:APK、AAB、IPA、iOS 或 macOS `.app`、Electron 目录、`app.asar`、JAR、ELF/Mach-O/PE/.NET 二进制、JS 打包文件、source map 或构建输出目录(含 Unity 与 Python 构建)。可选 `ignore` 与 `baseline` |
30
+ | `suggest_fixes` | 读取项目构建配置(Vite、webpack、Next.js、Angular、CRA、Android Gradle 与清单、React Native、electron-builder / Forge、.NET 项目文件、Flutter),以 unified diff 形式返回修改建议,不写入任何文件 |
28
31
  | `scan_url` | 抓取线上页面,检查同站点的打包脚本,跟随 `sourceMappingURL` 并尝试 `<bundle>.map`(不抓取第三方脚本) |
29
32
  | `list_checks` | 检查项目录,包括每项泄露了什么以及如何修复 |
30
33
 
@@ -47,6 +50,8 @@ Claude ──► reverse-shield ──► 你的构建产物 / 线上站点
47
50
  npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high
48
51
  npx @guardbee/mcp-reverse-shield scan ./app/build/outputs/apk/release/app-release.apk
49
52
  npx @guardbee/mcp-reverse-shield scan https://app.example.com --format=sarif > reverse-shield.sarif
53
+ npx @guardbee/mcp-reverse-shield scan ./dist --update-baseline # 记录基线,之后用 --baseline=… 只对新增问题报错
54
+ npx @guardbee/mcp-reverse-shield suggest . --format=patch | git apply # 应用前请先审阅
50
55
  ```
51
56
 
52
57
  ## 限制
@@ -54,7 +59,7 @@ npx @guardbee/mcp-reverse-shield scan https://app.example.com --format=sarif > r
54
59
  - 混淆仅根据名称判断,不分析控制流:只做重命名的构建会通过,字符串加密或控制流平坦化不在衡量范围内。
55
60
  - Web 检查只看发布出去的内容,无法判断某个密钥是否本就应当公开(例如 Firebase Web 密钥在服务端受限)。
56
61
  - 原生加固检查(PIE/NX/RELRO/canary)更多是漏洞利用缓解信号,因此以 low/medium 报告。
57
- - 暂不解包 iOS `.ipa` 和 Android App Bundle(`.aab`)。
62
+ - `suggest_fixes` 只处理各配置的常见写法;非常规结构不会生成 diff,Forge 与 Flutter 给出说明而非补丁。应用前务必审阅。
58
63
 
59
64
  ## 许可证
60
65
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=baseline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/baseline.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,130 @@
1
+ import { execFileSync, spawnSync } from "child_process";
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { dirname, join } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { afterEach, describe, expect, it } from "vitest";
7
+ import { applyIgnores, compareToBaseline, createBaseline, fingerprint, normalizeLocation } from "../baseline.js";
8
+ import { scanPath } from "../scanner.js";
9
+ const dirs = [];
10
+ const tmp = () => {
11
+ const d = mkdtempSync(join(tmpdir(), "rs-baseline-"));
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
+ const minified = Array.from({ length: 300 }, (_, i) => `function a${i}(b){return b+${i}}`).join(";");
20
+ const mapJson = JSON.stringify({ version: 3, mappings: "AAAA", sources: ["src/App.tsx"], sourcesContent: ["x"] });
21
+ function build(root, files) {
22
+ for (const [p, data] of Object.entries(files)) {
23
+ const full = join(root, ...p.split("/"));
24
+ mkdirSync(dirname(full), { recursive: true });
25
+ writeFileSync(full, data);
26
+ }
27
+ }
28
+ describe("normalizeLocation", () => {
29
+ it("replaces bundler content hashes so the next build matches", () => {
30
+ expect(normalizeLocation("/out/dist/assets/index-BvL4J5f8.js", "/out")).toBe("dist/assets/index-#.js");
31
+ expect(normalizeLocation("/out/_next/static/chunks/7321-dd7cb90a7de65945.js", "/out")).toBe("_next/static/chunks/7321-#.js");
32
+ expect(normalizeLocation("/out/chunk-HCOYIBKO.js", "/out/")).toBe("chunk-#.js");
33
+ expect(normalizeLocation("/out/static/js/main.a1b2c3d4.chunk.js", "/out")).toBe("static/js/main.#.chunk.js");
34
+ });
35
+ it("keeps ordinary names and strips query strings", () => {
36
+ expect(normalizeLocation("/out/src/LicenseValidator.js", "/out")).toBe("src/LicenseValidator.js");
37
+ expect(normalizeLocation("https://site.test/js/app.js?ver=2.0.21", "https://site.test/")).toBe("js/app.js");
38
+ expect(normalizeLocation("C:\\build\\out\\app.exe", "C:\\build")).toBe("out/app.exe");
39
+ });
40
+ });
41
+ describe("compareToBaseline", () => {
42
+ it("separates new, fixed and unchanged findings across two builds with different hashes", () => {
43
+ const v1 = tmp();
44
+ build(v1, {
45
+ "assets/index-AAAA1111.js": `${minified}\n//# sourceMappingURL=index-AAAA1111.js.map`,
46
+ "assets/index-AAAA1111.js.map": mapJson,
47
+ "assets/vendor-BBBB2222.js": 'fetch("http://localhost:3000")',
48
+ });
49
+ const baseline = createBaseline(scanPath(v1));
50
+ const v2 = tmp();
51
+ build(v2, {
52
+ "assets/index-CCCC3333.js": `${minified}\n//# sourceMappingURL=index-CCCC3333.js.map`,
53
+ "assets/index-CCCC3333.js.map": mapJson,
54
+ "assets/app-DDDD4444.js": 'const k="AKIAQ7XKZP3LMNB2RTUV";',
55
+ });
56
+ const diff = compareToBaseline(scanPath(v2), baseline);
57
+ expect(diff.unchanged.map((f) => f.patternId)).toEqual(["sourcemap_embeds_source"]);
58
+ expect(diff.added.map((f) => f.patternId)).toEqual(["bundle_secret"]);
59
+ expect(diff.fixed).toEqual(["internal_endpoint|assets/vendor-#.js"]);
60
+ expect(diff.previousGrade).toBe("F");
61
+ });
62
+ it("keeps two different keys in one file apart", () => {
63
+ const root = tmp();
64
+ // Assembled at runtime so the repository's secretlint hook does not read it as a leak.
65
+ const githubToken = ["ghp", "R8kd2LmQ9xTz4VbN7wPa1sYc6HfJ3uEo5Gi0"].join("_");
66
+ build(root, { "a.js": `const k="AKIAQ7XKZP3LMNB2RTUV";const g="${githubToken}";` });
67
+ const r = scanPath(root);
68
+ const fps = r.findings.map((f) => fingerprint(f, r.target));
69
+ expect(new Set(fps).size).toBe(2);
70
+ });
71
+ });
72
+ describe("applyIgnores", () => {
73
+ it("drops ignored rules, by id or id:path, and re-grades", () => {
74
+ const root = tmp();
75
+ build(root, {
76
+ "public/firebase-config.js": 'const k="AKIAQ7XKZP3LMNB2RTUV";',
77
+ "public/app.js": 'fetch("http://localhost:1")',
78
+ });
79
+ const r = scanPath(root);
80
+ expect(r.grade).toBe("F");
81
+ expect(applyIgnores(r, ["bundle_secret:firebase-config.js"])).toBe(1);
82
+ expect(r.findings.map((f) => f.patternId)).toEqual(["internal_endpoint"]);
83
+ expect(r.grade).toBe("B");
84
+ expect(r.notes).toContain("1 finding(s) suppressed by ignore rules");
85
+ expect(applyIgnores(r, ["internal_endpoint"])).toBe(1);
86
+ expect(r.grade).toBe("A");
87
+ });
88
+ it("does not drop a rule whose path does not match", () => {
89
+ const root = tmp();
90
+ build(root, { "app.js": 'const k="AKIAQ7XKZP3LMNB2RTUV";' });
91
+ const r = scanPath(root);
92
+ expect(applyIgnores(r, ["bundle_secret:other.js"])).toBe(0);
93
+ });
94
+ });
95
+ describe("CLI baseline flow", () => {
96
+ const cli = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "dist", "cli.js");
97
+ const run = (args, cwd) => spawnSync(process.execPath, [cli, ...args], { cwd, encoding: "utf8" });
98
+ it("fails only on findings the baseline does not already have", () => {
99
+ const project = tmp();
100
+ build(project, { "dist/app-AAAA1111.js": 'fetch("http://localhost:3000");const m=1;' + minified });
101
+ const write = run(["scan", "dist", "--update-baseline"], project);
102
+ expect(write.stderr).toContain("Baseline written: .reverse-shield-baseline.json (1 finding(s), grade B)");
103
+ expect(JSON.parse(readFileSync(join(project, ".reverse-shield-baseline.json"), "utf8")).fingerprints).toEqual(["internal_endpoint|app-#.js"]);
104
+ // Same exposure, new hash: nothing new, passes even with --fail-on=any.
105
+ rmSync(join(project, "dist"), { recursive: true });
106
+ build(project, { "dist/app-BBBB2222.js": 'fetch("http://localhost:3000");const m=2;' + minified });
107
+ const same = run(["scan", "dist", "--baseline=.reverse-shield-baseline.json", "--fail-on=any"], project);
108
+ expect(same.status).toBe(0);
109
+ expect(same.stdout).toContain("0 new, 0 fixed, 1 unchanged");
110
+ // A new key: fails, and SARIF marks it new while the old one stays unchanged.
111
+ build(project, { "dist/keys-CCCC3333.js": 'const k="AKIAQ7XKZP3LMNB2RTUV";' });
112
+ const worse = run(["scan", "dist", "--baseline=.reverse-shield-baseline.json", "--fail-on=high"], project);
113
+ expect(worse.status).toBe(1);
114
+ expect(worse.stdout).toContain("1 new, 0 fixed, 1 unchanged");
115
+ const sarif = JSON.parse(execFileSync(process.execPath, [cli, "scan", "dist", "--baseline=.reverse-shield-baseline.json", "--format=sarif", "--fail-on=none"], { cwd: project, encoding: "utf8" }));
116
+ const states = sarif.runs[0].results.map((r) => `${r.ruleId}:${r.baselineState}`).sort();
117
+ expect(states).toEqual(["bundle_secret:new", "internal_endpoint:unchanged"]);
118
+ }, 30_000); // five CLI processes: ~1 s each on a cold CI runner
119
+ it("reads ignore and baseline from guardbee.yml", () => {
120
+ const project = tmp();
121
+ build(project, {
122
+ "guardbee.yml": "reverse-shield:\n fail-on: any\n ignore:\n - internal_endpoint\n",
123
+ "dist/app.js": 'fetch("http://localhost:3000")',
124
+ });
125
+ const r = run(["scan", "dist"], project);
126
+ expect(r.status).toBe(0);
127
+ expect(r.stdout).toContain("1 finding(s) suppressed by ignore rules");
128
+ }, 30_000);
129
+ });
130
+ //# sourceMappingURL=baseline.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.test.js","sourceRoot":"","sources":["../../src/__tests__/baseline.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fixes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fixes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,163 @@
1
+ import { execFileSync } from "child_process";
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { dirname, join } from "path";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import { patchOf, suggestFixes, unifiedDiff } from "../fixes.js";
7
+ const dirs = [];
8
+ afterEach(() => {
9
+ for (const d of dirs.splice(0))
10
+ rmSync(d, { recursive: true, force: true });
11
+ });
12
+ function project(files) {
13
+ const root = mkdtempSync(join(tmpdir(), "rs-fixes-"));
14
+ dirs.push(root);
15
+ for (const [p, data] of Object.entries(files)) {
16
+ const full = join(root, ...p.split("/"));
17
+ mkdirSync(dirname(full), { recursive: true });
18
+ writeFileSync(full, data);
19
+ }
20
+ return root;
21
+ }
22
+ /** Applies the suggested patch with real git and returns the resulting files. */
23
+ function applyPatch(root, files) {
24
+ const git = (...args) => execFileSync("git", args, { cwd: root, stdio: "pipe" });
25
+ git("init", "-q");
26
+ git("-c", "user.email=t@t", "-c", "user.name=t", "add", "-A");
27
+ git("-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "init");
28
+ writeFileSync(join(root, "fix.patch"), patchOf(suggestFixes(root)));
29
+ git("apply", "fix.patch");
30
+ return Object.fromEntries(files.map((f) => [f, readFileSync(join(root, ...f.split("/")), "utf8")]));
31
+ }
32
+ const byFile = (root) => Object.fromEntries(suggestFixes(root).suggestions.map((s) => [s.file, s]));
33
+ describe("unifiedDiff", () => {
34
+ it("produces one hunk with context", () => {
35
+ const diff = unifiedDiff("a.txt", "1\n2\n3\n4\n5\n6\n7\n8\n", "1\n2\n3\n4\nfive\n6\n7\n8\n");
36
+ expect(diff).toBe("--- a/a.txt\n+++ b/a.txt\n@@ -2,7 +2,7 @@\n 2\n 3\n 4\n-5\n+five\n 6\n 7\n 8\n");
37
+ expect(unifiedDiff("a", "same", "same")).toBe("");
38
+ });
39
+ it("does not invent a line for the final newline, and marks a missing one", () => {
40
+ expect(unifiedDiff("a", "x\ny\n", "x\nz\n")).toBe("--- a/a\n+++ b/a\n@@ -1,2 +1,2 @@\n x\n-y\n+z\n");
41
+ expect(unifiedDiff("a", "x\ny", "x\nz")).toBe("--- a/a\n+++ b/a\n@@ -1,2 +1,2 @@\n x\n-y\n\\n+z\n\\n");
42
+ });
43
+ });
44
+ describe("web build configs", () => {
45
+ it("switches Vite to hidden source maps and turns minification back on", () => {
46
+ const root = project({ "vite.config.ts": "export default defineConfig({\n build: {\n sourcemap: true,\n minify: false,\n },\n});\n" });
47
+ const out = applyPatch(root, ["vite.config.ts"]);
48
+ expect(out["vite.config.ts"]).toContain("sourcemap: 'hidden',");
49
+ expect(out["vite.config.ts"]).toContain("minify: 'esbuild',");
50
+ });
51
+ it("rewrites webpack devtools that ship maps or eval, and leaves hidden/false alone", () => {
52
+ const root = project({
53
+ "webpack.prod.js": "module.exports = { mode: 'production', devtool: 'eval-cheap-module-source-map', optimization: { minimize: false } };\n",
54
+ "webpack.config.js": "module.exports = { devtool: 'hidden-source-map' };\n",
55
+ "webpack.dev.js": "module.exports = { devtool: 'eval' };\n",
56
+ });
57
+ const s = byFile(root);
58
+ expect(Object.keys(s)).toEqual(["webpack.prod.js"]);
59
+ const out = applyPatch(root, ["webpack.prod.js"]);
60
+ expect(out["webpack.prod.js"]).toBe("module.exports = { mode: 'production', devtool: 'hidden-source-map', optimization: { minimize: true } };\n");
61
+ });
62
+ it("turns off Next.js browser source maps and Angular production maps", () => {
63
+ const root = project({
64
+ "next.config.mjs": "export default {\n productionBrowserSourceMaps: true,\n};\n",
65
+ "angular.json": '{\n "configurations": {\n "development": {\n "sourceMap": true\n },\n "production": {\n "sourceMap": true,\n "optimization": true\n }\n }\n}\n',
66
+ });
67
+ const out = applyPatch(root, ["next.config.mjs", "angular.json"]);
68
+ expect(out["next.config.mjs"]).toContain("productionBrowserSourceMaps: false");
69
+ expect(out["angular.json"]).toContain('"development": {\n "sourceMap": true');
70
+ expect(out["angular.json"]).toContain('"production": {\n "sourceMap": false');
71
+ });
72
+ it("explains the CRA env switch only when it is missing", () => {
73
+ expect(byFile(project({ "package.json": '{"dependencies":{"react-scripts":"5.0.1"}}' }))["package.json"].note).toContain("GENERATE_SOURCEMAP=false to .env.production");
74
+ expect(suggestFixes(project({ "package.json": '{"dependencies":{"react-scripts":"5.0.1"}}', ".env.production": "GENERATE_SOURCEMAP=false\n" })).suggestions).toEqual([]);
75
+ });
76
+ });
77
+ describe("Android", () => {
78
+ it("edits buildTypes.release in Groovy, not the signing config of the same name", () => {
79
+ const gradle = [
80
+ "apply plugin: 'com.android.application'",
81
+ "android {",
82
+ " signingConfigs {",
83
+ " release {",
84
+ " storeFile file('key.jks')",
85
+ " }",
86
+ " }",
87
+ " buildTypes {",
88
+ " release {",
89
+ " minifyEnabled false",
90
+ " debuggable true",
91
+ " signingConfig signingConfigs.release",
92
+ " }",
93
+ " }",
94
+ "}",
95
+ "",
96
+ ].join("\n");
97
+ const out = applyPatch(project({ "app/build.gradle": gradle }), ["app/build.gradle"]);
98
+ expect(out["app/build.gradle"]).toContain(" release {\n storeFile file('key.jks')\n }");
99
+ expect(out["app/build.gradle"]).toContain(" release {\n minifyEnabled true\n signingConfig signingConfigs.release\n }");
100
+ });
101
+ it("adds R8 to a Kotlin DSL release block that has none", () => {
102
+ const gradle = 'plugins { id("com.android.application") }\nandroid {\n buildTypes {\n getByName("release") {\n signingConfig = signingConfigs.getByName("debug")\n }\n }\n}\n';
103
+ const out = applyPatch(project({ "app/build.gradle.kts": gradle }), ["app/build.gradle.kts"]);
104
+ expect(out["app/build.gradle.kts"]).toContain('getByName("release") {\n isMinifyEnabled = true\n isShrinkResources = true\n proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")\n signingConfig');
105
+ });
106
+ it("leaves a release block that already minifies alone", () => {
107
+ expect(suggestFixes(project({ "app/build.gradle.kts": 'android {\n buildTypes {\n release {\n isMinifyEnabled = true\n }\n }\n}\n' })).suggestions).toEqual([]);
108
+ });
109
+ it("sets allowBackup in the main manifest, keeping multi-line attribute layout", () => {
110
+ const manifest = '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n <application\n android:label="Shop"\n android:debuggable="true">\n </application>\n</manifest>\n';
111
+ const root = project({ "app/src/main/AndroidManifest.xml": manifest, "app/src/debug/AndroidManifest.xml": '<manifest><application android:label="dbg" /></manifest>\n' });
112
+ expect(Object.keys(byFile(root))).toEqual(["app/src/main/AndroidManifest.xml"]);
113
+ const out = applyPatch(root, ["app/src/main/AndroidManifest.xml"]);
114
+ expect(out["app/src/main/AndroidManifest.xml"]).toBe('<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n <application\n android:allowBackup="false"\n android:label="Shop">\n </application>\n</manifest>\n');
115
+ });
116
+ it("enables Hermes for React Native", () => {
117
+ const out = applyPatch(project({ "android/gradle.properties": "org.gradle.jvmargs=-Xmx2048m\nhermesEnabled=false\n" }), ["android/gradle.properties"]);
118
+ expect(out["android/gradle.properties"]).toContain("hermesEnabled=true");
119
+ });
120
+ });
121
+ describe("Electron", () => {
122
+ it("adds asar and fuses to electron-builder config in package.json", () => {
123
+ const pkg = '{\n "name": "app",\n "devDependencies": {\n "electron-builder": "^25.0.0"\n },\n "build": {\n "appId": "x",\n "asar": false\n }\n}\n';
124
+ const out = applyPatch(project({ "package.json": pkg }), ["package.json"]);
125
+ const parsed = JSON.parse(out["package.json"]);
126
+ expect(parsed.build.asar).toBe(true);
127
+ expect(parsed.build.electronFuses).toMatchObject({ runAsNode: false, enableNodeCliInspectArguments: false, onlyLoadAppFromAsar: true });
128
+ });
129
+ it("appends fuses to electron-builder.yml and points Forge users at the plugin", () => {
130
+ const root = project({ "electron-builder.yml": "appId: x\nasar: false\n", "forge.config.js": "module.exports = { plugins: [] };\n" });
131
+ const out = applyPatch(root, ["electron-builder.yml"]);
132
+ expect(out["electron-builder.yml"]).toBe("appId: x\nasar: true\nelectronFuses:\n runAsNode: false\n enableCookieEncryption: true\n enableNodeOptionsEnvironmentVariable: false\n enableNodeCliInspectArguments: false\n enableEmbeddedAsarIntegrityValidation: true\n onlyLoadAppFromAsar: true\n");
133
+ expect(byFile(root)["forge.config.js"]).toMatchObject({ diff: "" });
134
+ expect(byFile(root)["forge.config.js"].note).toContain("new FusesPlugin");
135
+ });
136
+ });
137
+ describe(".NET and Flutter", () => {
138
+ it("stops embedding the PDB and sources", () => {
139
+ const out = applyPatch(project({ "src/App/App.csproj": "<Project>\n <PropertyGroup>\n <DebugType>embedded</DebugType>\n <EmbedAllSources>true</EmbedAllSources>\n </PropertyGroup>\n</Project>\n" }), ["src/App/App.csproj"]);
140
+ expect(out["src/App/App.csproj"]).toContain("<DebugType>portable</DebugType>");
141
+ expect(out["src/App/App.csproj"]).toContain("<EmbedAllSources>false</EmbedAllSources>");
142
+ });
143
+ it("gives Flutter build flags as a note", () => {
144
+ const [s] = suggestFixes(project({ "pubspec.yaml": "name: shop\nflutter:\n uses-material-design: true\n" })).suggestions;
145
+ expect(s.note).toContain("--obfuscate --split-debug-info");
146
+ });
147
+ });
148
+ describe("suggestFixes", () => {
149
+ it("skips build output and dependencies, and lists what it read", () => {
150
+ const root = project({
151
+ "node_modules/x/vite.config.js": "export default { build: { sourcemap: true } }",
152
+ "dist/next.config.js": "module.exports = { productionBrowserSourceMaps: true }",
153
+ "vite.config.js": "export default { build: { sourcemap: 'hidden' } }",
154
+ });
155
+ const report = suggestFixes(root);
156
+ expect(report.suggestions).toEqual([]);
157
+ expect(report.inspected).toEqual(["vite.config.js"]);
158
+ });
159
+ it("rejects a path that is not a directory", () => {
160
+ expect(() => suggestFixes(join(tmpdir(), "definitely-missing-rs-dir"))).toThrow(/Not a project directory/);
161
+ });
162
+ });
163
+ //# sourceMappingURL=fixes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixes.test.js","sourceRoot":"","sources":["../../src/__tests__/fixes.test.ts"],"names":[],"mappings":"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"}