@chengyu08/ipa-cli 0.2.3 → 0.2.5
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/README.md +78 -0
- package/dist/src/cli.js +5 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/assets.d.ts +1 -0
- package/dist/src/commands/assets.js +34 -0
- package/dist/src/commands/assets.js.map +1 -1
- package/dist/src/commands/obfuscate.d.ts +2 -0
- package/dist/src/commands/obfuscate.js +50 -0
- package/dist/src/commands/obfuscate.js.map +1 -0
- package/dist/src/core/ip.js +2 -2
- package/dist/src/core/obfuscate/tech-obfuscate.d.ts +43 -0
- package/dist/src/core/obfuscate/tech-obfuscate.js +647 -0
- package/dist/src/core/obfuscate/tech-obfuscate.js.map +1 -0
- package/dist/src/core/package-resources.d.ts +50 -0
- package/dist/src/core/package-resources.js +415 -0
- package/dist/src/core/package-resources.js.map +1 -0
- package/dist/src/core/unpack.d.ts +6 -0
- package/dist/src/core/unpack.js +30 -16
- package/dist/src/core/unpack.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- 生成、校验和批量渲染 App Store 上架图
|
|
23
23
|
- 批量扫描当前目录或多个子项目中的静态图片风险
|
|
24
24
|
- 输出机器可消费的静态图片整改清单 JSON
|
|
25
|
+
- 提取 IPA / APK 包中的图片、字体资源与 `Assets.car` 原文件,并按类型整理到清晰目录
|
|
25
26
|
|
|
26
27
|
## 安装要求
|
|
27
28
|
|
|
@@ -603,6 +604,8 @@ cp ./scripts/ipatool.env.example ./scripts/ipatool.env
|
|
|
603
604
|
- `screenshots from-simulator`:从 iOS 模拟器采集一张原始截图
|
|
604
605
|
- `screenshots check`:校验上架图目录的尺寸、格式、数量和命名
|
|
605
606
|
- `screenshots render`:根据配置批量渲染上架图
|
|
607
|
+
- `extract`:提取 IPA / APK 包中的图片、字体资源,并输出应用摘要
|
|
608
|
+
- `obfuscate`:按技术栈输出 iOS 混淆方案,重点支持 Flutter、uni-app 等跨平台项目
|
|
606
609
|
- `assets review`:批量扫描当前目录或多个子项目中的静态图片风险
|
|
607
610
|
- `assets manifest`:输出机器可消费的静态图片整改清单 JSON
|
|
608
611
|
- `assets obfuscate`:批量混淆图片资源,使哈希等指标不一致,用于降低重复图片风险
|
|
@@ -1926,6 +1929,41 @@ ipa-cli assets manifest . --scope children --output ./artifacts/assets-manifest.
|
|
|
1926
1929
|
- `consolidate-duplicates`
|
|
1927
1930
|
- `review-metadata-visuals`
|
|
1928
1931
|
|
|
1932
|
+
### `extract`
|
|
1933
|
+
|
|
1934
|
+
提取 `.ipa` 或 `.apk` 包内可直接落盘的图片、字体资源,并按 `images/`、`fonts/`、`assets-car/` 分目录整理。命令还会额外输出 `app.json`,汇总包名、应用名、版本和主图标路径;如果识别到主图标,会在输出根目录直接生成一份 `icon.*`,便于快速查看。针对 IPA,如果检测到 `Assets.car`,还会原样导出该文件并额外生成摘要 JSON,便于后续人工复核。`assets extract` 仍保留为兼容别名。
|
|
1935
|
+
|
|
1936
|
+
```bash
|
|
1937
|
+
ipa-cli extract <input> [--output <dir>] [--only all|images|fonts] [--force] [--format text|json]
|
|
1938
|
+
```
|
|
1939
|
+
|
|
1940
|
+
参数:
|
|
1941
|
+
|
|
1942
|
+
- `<input>`:`.ipa` 或 `.apk` 文件路径
|
|
1943
|
+
- `--output <dir>`:提取输出目录,默认是 `./<包名>-assets`
|
|
1944
|
+
- `--only all|images|fonts`:控制提取范围,默认 `all`
|
|
1945
|
+
- `--force`:输出目录非空时覆盖已有内容
|
|
1946
|
+
- `--format text|json`:输出格式,默认 `text`
|
|
1947
|
+
|
|
1948
|
+
示例:
|
|
1949
|
+
|
|
1950
|
+
```bash
|
|
1951
|
+
ipa-cli extract ./demo.ipa --output ./artifacts/demo-assets
|
|
1952
|
+
ipa-cli extract ./demo.apk --only images --format json
|
|
1953
|
+
```
|
|
1954
|
+
|
|
1955
|
+
默认输出结构:
|
|
1956
|
+
|
|
1957
|
+
```text
|
|
1958
|
+
demo-assets/
|
|
1959
|
+
app.json
|
|
1960
|
+
icon.png
|
|
1961
|
+
manifest.json
|
|
1962
|
+
images/
|
|
1963
|
+
fonts/
|
|
1964
|
+
assets-car/
|
|
1965
|
+
```
|
|
1966
|
+
|
|
1929
1967
|
### `assets obfuscate`
|
|
1930
1968
|
|
|
1931
1969
|
批量混淆图片资源,通过噪点注入、色彩偏移和重编码使图片的 SHA-256、感知哈希等指标不一致,从而降低被识别为重复图片的风险。
|
|
@@ -1985,6 +2023,44 @@ ipa-cli assets obfuscate ./images --format json --output ./images-obfuscated
|
|
|
1985
2023
|
- 资源命名模式仅在启用 `--rename` 时修改,Assets.car 结构和页面引用关系不会被更新
|
|
1986
2024
|
- 建议使用 `assets review` 命令重新扫描,确认各项指标差异是否足够
|
|
1987
2025
|
|
|
2026
|
+
### `obfuscate`
|
|
2027
|
+
|
|
2028
|
+
根据 IPA 或指纹 JSON 的技术栈画像,输出对应的 iOS 混淆方案。当前重点覆盖:
|
|
2029
|
+
|
|
2030
|
+
- Flutter:构建时 Dart 混淆 + `flutter_assets` 业务资源治理
|
|
2031
|
+
- uni-app:`app-plus.confusion.resources` 定向原生混淆 + 云打包 / 安心打包 + H5 资源治理
|
|
2032
|
+
- 其他栈:React Native / Hybrid / 原生 iOS 的通用保护与降重建议
|
|
2033
|
+
|
|
2034
|
+
其中 Flutter 方案会明确区分:
|
|
2035
|
+
|
|
2036
|
+
- `App.framework/App`:Dart AOT 业务产物,`--obfuscate` 主要影响这里
|
|
2037
|
+
- `Runner`:原生壳入口,如需变化要改壳层代码、配置、桥接或插件集合
|
|
2038
|
+
- `Flutter.framework`:公共运行时,更适合做技术栈识别,不是主要业务降重目标
|
|
2039
|
+
|
|
2040
|
+
```bash
|
|
2041
|
+
ipa-cli obfuscate [input] [--stack auto|flutter|uni-app|react-native|hybrid|native-ios] [--format text|json|markdown]
|
|
2042
|
+
```
|
|
2043
|
+
|
|
2044
|
+
参数:
|
|
2045
|
+
|
|
2046
|
+
- `[input]`:可选,IPA 文件路径或指纹 JSON 路径;省略时必须手动传 `--stack`
|
|
2047
|
+
- `--stack <stack>`:技术栈,默认 `auto`
|
|
2048
|
+
- `--format <format>`:输出格式,默认 `text`
|
|
2049
|
+
|
|
2050
|
+
示例:
|
|
2051
|
+
|
|
2052
|
+
```bash
|
|
2053
|
+
ipa-cli obfuscate ./build/ios/ipa/MyApp.ipa
|
|
2054
|
+
ipa-cli obfuscate ./fingerprints/flutter.json --format markdown
|
|
2055
|
+
ipa-cli obfuscate --stack uni-app --format text
|
|
2056
|
+
```
|
|
2057
|
+
|
|
2058
|
+
说明:
|
|
2059
|
+
|
|
2060
|
+
- `obfuscate` 不会直接修改已构建的 IPA 二进制,而是输出按技术栈划分的可执行方案
|
|
2061
|
+
- 纯图片资源仍建议配合 `assets review` / `assets obfuscate` 使用
|
|
2062
|
+
- 若已知工程类型,优先用 `--stack` 明确指定,避免自动识别误差
|
|
2063
|
+
|
|
1988
2064
|
### `baseline import`
|
|
1989
2065
|
|
|
1990
2066
|
把现成的指纹 JSON / JSONL 导入本地基线库。
|
|
@@ -2185,6 +2261,7 @@ ipa-cli baseline list --library ./data/library --format panel
|
|
|
2185
2261
|
- `screenshots from-ipa`:`text | json | panel`
|
|
2186
2262
|
- `screenshots check`:`text | json | panel`
|
|
2187
2263
|
- `screenshots render`:`text | json | panel`
|
|
2264
|
+
- `extract`:`text | json`
|
|
2188
2265
|
- `assets review`:`text | json | panel`
|
|
2189
2266
|
- `assets manifest`:固定输出 `json`
|
|
2190
2267
|
- `baseline doctor`:`panel | json | tsv`
|
|
@@ -2279,6 +2356,7 @@ ipa-cli --help
|
|
|
2279
2356
|
|
|
2280
2357
|
```bash
|
|
2281
2358
|
ipa-cli --help
|
|
2359
|
+
ipa-cli extract --help
|
|
2282
2360
|
ipa-cli analyze --help
|
|
2283
2361
|
ipa-cli compare --help
|
|
2284
2362
|
ipa-cli scan --help
|
package/dist/src/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { registerActionsCommand } from "./commands/actions.js";
|
|
4
4
|
import { registerAnalyzeCommand } from "./commands/analyze.js";
|
|
5
|
-
import { registerAssetsCommand } from "./commands/assets.js";
|
|
5
|
+
import { registerAssetsCommand, registerExtractCommand } from "./commands/assets.js";
|
|
6
6
|
import { registerBaselineCommand } from "./commands/baseline.js";
|
|
7
7
|
import { registerCompareCommand } from "./commands/compare.js";
|
|
8
8
|
import { registerConvertCommand } from "./commands/convert.js";
|
|
@@ -10,6 +10,7 @@ import { registerDimensionAnalyzeCommand } from "./commands/dimension-analyze.js
|
|
|
10
10
|
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
11
11
|
import { registerIpCommand } from "./commands/ip.js";
|
|
12
12
|
import { registerKnowledgeCommand } from "./commands/knowledge.js";
|
|
13
|
+
import { registerObfuscateCommand } from "./commands/obfuscate.js";
|
|
13
14
|
import { registerScanCommand } from "./commands/scan.js";
|
|
14
15
|
import { registerScanBatchCommand } from "./commands/scan-batch.js";
|
|
15
16
|
import { registerScreenshotsCommand } from "./commands/screenshots.js";
|
|
@@ -20,10 +21,11 @@ const program = new Command();
|
|
|
20
21
|
program
|
|
21
22
|
.name("ipa-cli")
|
|
22
23
|
.description("检查 IPA 的提审风险信号与相似性指标。")
|
|
23
|
-
.version("0.2.
|
|
24
|
+
.version("0.2.5")
|
|
24
25
|
.showHelpAfterError();
|
|
25
26
|
registerAnalyzeCommand(program);
|
|
26
27
|
registerActionsCommand(program);
|
|
28
|
+
registerExtractCommand(program);
|
|
27
29
|
registerAssetsCommand(program);
|
|
28
30
|
registerCompareCommand(program);
|
|
29
31
|
registerConvertCommand(program);
|
|
@@ -31,6 +33,7 @@ registerDimensionAnalyzeCommand(program);
|
|
|
31
33
|
registerDoctorCommand(program);
|
|
32
34
|
registerIpCommand(program);
|
|
33
35
|
registerKnowledgeCommand(program);
|
|
36
|
+
registerObfuscateCommand(program);
|
|
34
37
|
registerScanCommand(program);
|
|
35
38
|
registerScanBatchCommand(program);
|
|
36
39
|
registerScreenshotsCommand(program);
|
package/dist/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { obfuscateImages, renderObfuscateReportText } from "../core/obfuscate/image-obfuscate.js";
|
|
4
|
+
import { extractPackageResources, renderExtractPackageResourcesText } from "../core/package-resources.js";
|
|
4
5
|
import { renderAssetsReviewText } from "../core/reporting/assets-review-text.js";
|
|
5
6
|
import { renderAssetsReviewPanel } from "../core/reporting/panel.js";
|
|
6
7
|
import { reviewAssetProjects } from "../core/review/assets-review.js";
|
|
7
8
|
import { withProgress } from "../lib/progress.js";
|
|
8
9
|
export function registerAssetsCommand(program) {
|
|
9
10
|
const assets = program.command("assets").description("批量扫描静态图片风险并生成整改清单。");
|
|
11
|
+
configureExtractCommand(assets
|
|
12
|
+
.command("extract")
|
|
13
|
+
.description("提取 IPA 或 APK 包中的图片、字体资源,并输出应用摘要。"));
|
|
10
14
|
assets
|
|
11
15
|
.command("review")
|
|
12
16
|
.description("扫描目录下的一个或多个项目,输出静态图片风险报告。")
|
|
@@ -81,6 +85,11 @@ export function registerAssetsCommand(program) {
|
|
|
81
85
|
process.stdout.write(`${output}\n`);
|
|
82
86
|
});
|
|
83
87
|
}
|
|
88
|
+
export function registerExtractCommand(program) {
|
|
89
|
+
configureExtractCommand(program
|
|
90
|
+
.command("extract")
|
|
91
|
+
.description("提取 IPA 或 APK 包中的图片、字体资源,并输出应用摘要。"));
|
|
92
|
+
}
|
|
84
93
|
function normalizeIntensity(value) {
|
|
85
94
|
if (value === "low" || value === "high")
|
|
86
95
|
return value;
|
|
@@ -89,6 +98,31 @@ function normalizeIntensity(value) {
|
|
|
89
98
|
function normalizeScope(scope) {
|
|
90
99
|
return scope === "cwd" ? "cwd" : "children";
|
|
91
100
|
}
|
|
101
|
+
function configureExtractCommand(command) {
|
|
102
|
+
command
|
|
103
|
+
.argument("<input>", "IPA 或 APK 文件路径")
|
|
104
|
+
.option("--output <dir>", "提取输出目录")
|
|
105
|
+
.option("--only <kind>", "提取类型:all|images|fonts", "all")
|
|
106
|
+
.option("--force", "输出目录非空时覆盖已有内容", false)
|
|
107
|
+
.option("--format <format>", "输出格式:text|json", "text")
|
|
108
|
+
.action(async (input, options) => {
|
|
109
|
+
const report = await withProgress("正在提取包内资源", options.format !== "json", async () => {
|
|
110
|
+
return await extractPackageResources(path.resolve(input), {
|
|
111
|
+
outputDir: options.output ? path.resolve(options.output) : undefined,
|
|
112
|
+
only: normalizeExtractKind(options.only),
|
|
113
|
+
force: options.force ?? false
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
const output = options.format === "json" ? JSON.stringify(report, null, 2) : renderExtractPackageResourcesText(report);
|
|
117
|
+
process.stdout.write(`${output}\n`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function normalizeExtractKind(value) {
|
|
121
|
+
if (value === "images" || value === "fonts") {
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
return "all";
|
|
125
|
+
}
|
|
92
126
|
function parsePositiveInteger(value, fallback) {
|
|
93
127
|
const parsed = Number.parseInt(value, 10);
|
|
94
128
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../src/commands/assets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAClG,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../src/commands/assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { loadFingerprintInput } from "./analyze.js";
|
|
3
|
+
import { createObfuscationPlan, renderObfuscationPlan } from "../core/obfuscate/tech-obfuscate.js";
|
|
4
|
+
export function registerObfuscateCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command("obfuscate")
|
|
7
|
+
.description("针对不同技术栈生成 iOS 混淆方案,重点支持 Flutter、uni-app 等跨平台项目。")
|
|
8
|
+
.argument("[input]", "IPA 文件路径或指纹 JSON 路径;省略时需配合 --stack")
|
|
9
|
+
.option("--stack <stack>", "技术栈:auto|flutter|uni-app|react-native|hybrid|native-ios", "auto")
|
|
10
|
+
.option("--format <format>", "输出格式:text|json|markdown", "text")
|
|
11
|
+
.action(async (input, options) => {
|
|
12
|
+
const stack = normalizeStack(options.stack);
|
|
13
|
+
const format = normalizeFormat(options.format);
|
|
14
|
+
if (!input && stack === "auto") {
|
|
15
|
+
throw new Error("未提供输入时,必须使用 --stack 指定技术栈。");
|
|
16
|
+
}
|
|
17
|
+
const resolvedInputPath = input ? path.resolve(input) : undefined;
|
|
18
|
+
const fingerprint = resolvedInputPath
|
|
19
|
+
? await loadFingerprintInput(resolvedInputPath, {
|
|
20
|
+
showProgress: resolvedInputPath.endsWith(".ipa") && format !== "json",
|
|
21
|
+
progressText: "正在分析技术栈并生成混淆方案"
|
|
22
|
+
})
|
|
23
|
+
: undefined;
|
|
24
|
+
const plan = createObfuscationPlan({
|
|
25
|
+
fingerprint,
|
|
26
|
+
sourcePath: resolvedInputPath,
|
|
27
|
+
requestedStack: stack
|
|
28
|
+
});
|
|
29
|
+
process.stdout.write(`${renderObfuscationPlan(plan, format)}\n`);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function normalizeStack(value) {
|
|
33
|
+
if (value === "auto" || value === "flutter" || value === "uni-app" || value === "react-native" || value === "hybrid" || value === "native-ios") {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
if (value === "uniapp" || value === "uni") {
|
|
37
|
+
return "uni-app";
|
|
38
|
+
}
|
|
39
|
+
if (value === "reactnative" || value === "rn") {
|
|
40
|
+
return "react-native";
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`不支持的技术栈: ${value}`);
|
|
43
|
+
}
|
|
44
|
+
function normalizeFormat(value) {
|
|
45
|
+
if (value === "text" || value === "json" || value === "markdown") {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`不支持的 obfuscate 输出格式: ${value}`);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=obfuscate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obfuscate.js","sourceRoot":"","sources":["../../../src/commands/obfuscate.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAiD,MAAM,qCAAqC,CAAC;AAOlJ,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,iDAAiD,CAAC;SAC9D,QAAQ,CAAC,SAAS,EAAE,oCAAoC,CAAC;SACzD,MAAM,CAAC,iBAAiB,EAAE,yDAAyD,EAAE,MAAM,CAAC;SAC5F,MAAM,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,CAAC;SAC9D,MAAM,CAAC,KAAK,EAAE,KAAyB,EAAE,OAAgC,EAAE,EAAE;QAC5E,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE/C,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,iBAAiB,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,MAAM,WAAW,GAAG,iBAAiB;YACnC,CAAC,CAAC,MAAM,oBAAoB,CAAC,iBAAiB,EAAE;gBAC5C,YAAY,EAAE,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,KAAK,MAAM;gBACrE,YAAY,EAAE,gBAAgB;aAC/B,CAAC;YACJ,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,IAAI,GAAG,qBAAqB,CAAC;YACjC,WAAW;YACX,UAAU,EAAE,iBAAiB;YAC7B,cAAc,EAAE,KAAK;SACtB,CAAC,CAAC;QAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,cAAc,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;QAC/I,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QAC1C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,KAAK,aAAa,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;QACjE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
package/dist/src/core/ip.js
CHANGED
|
@@ -118,7 +118,7 @@ async function resolvePublicIpLocationFromIpCn(ip, requestTextImpl) {
|
|
|
118
118
|
const response = await requestPublicLocation(createPublicLocationUrl(ip), requestTextImpl, {
|
|
119
119
|
"Cache-Control": "no-cache, no-store, max-age=0",
|
|
120
120
|
Pragma: "no-cache",
|
|
121
|
-
"User-Agent": "Mozilla/5.0 (compatible; ipa-cli/0.2.
|
|
121
|
+
"User-Agent": "Mozilla/5.0 (compatible; ipa-cli/0.2.5; +https://github.com/ChengYu08/ipa_cli)"
|
|
122
122
|
});
|
|
123
123
|
const displayLocation = extractIpCnDisplayLocation(response.body);
|
|
124
124
|
if (!displayLocation) {
|
|
@@ -135,7 +135,7 @@ async function resolvePublicIpLocationFromIpSb(ip, requestTextImpl) {
|
|
|
135
135
|
const response = await requestPublicLocation(createPublicLocationFallbackUrl(ip), requestTextImpl, {
|
|
136
136
|
"Cache-Control": "no-cache, no-store, max-age=0",
|
|
137
137
|
Pragma: "no-cache",
|
|
138
|
-
"User-Agent": "ipa-cli/0.2.
|
|
138
|
+
"User-Agent": "ipa-cli/0.2.5"
|
|
139
139
|
});
|
|
140
140
|
let payload;
|
|
141
141
|
try {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AppFingerprint, TechProfile } from "../../models/fingerprint.js";
|
|
2
|
+
export type ObfuscationStack = "flutter" | "uni-app" | "react-native" | "hybrid" | "native-ios" | "unknown";
|
|
3
|
+
export type ObfuscationFormat = "text" | "json" | "markdown";
|
|
4
|
+
type DetectionMode = "auto" | "manual";
|
|
5
|
+
type DetectionConfidence = "high" | "medium" | "low";
|
|
6
|
+
export interface ObfuscationPlanPhase {
|
|
7
|
+
title: string;
|
|
8
|
+
objective: string;
|
|
9
|
+
actions: string[];
|
|
10
|
+
commands: string[];
|
|
11
|
+
warnings: string[];
|
|
12
|
+
snippetLanguage?: string;
|
|
13
|
+
snippet?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ObfuscationPlanReference {
|
|
16
|
+
label: string;
|
|
17
|
+
url: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ObfuscationPlan {
|
|
20
|
+
sourcePath?: string;
|
|
21
|
+
requestedStack: ObfuscationStack | "auto";
|
|
22
|
+
resolvedStack: ObfuscationStack;
|
|
23
|
+
detection: {
|
|
24
|
+
mode: DetectionMode;
|
|
25
|
+
baseProfile: TechProfile | "none";
|
|
26
|
+
confidence: DetectionConfidence;
|
|
27
|
+
reason: string;
|
|
28
|
+
signals: string[];
|
|
29
|
+
};
|
|
30
|
+
summary: string;
|
|
31
|
+
objective: string;
|
|
32
|
+
phases: ObfuscationPlanPhase[];
|
|
33
|
+
followUps: string[];
|
|
34
|
+
limitations: string[];
|
|
35
|
+
references: ObfuscationPlanReference[];
|
|
36
|
+
}
|
|
37
|
+
export declare function createObfuscationPlan(options: {
|
|
38
|
+
fingerprint?: AppFingerprint;
|
|
39
|
+
sourcePath?: string;
|
|
40
|
+
requestedStack?: ObfuscationStack | "auto";
|
|
41
|
+
}): ObfuscationPlan;
|
|
42
|
+
export declare function renderObfuscationPlan(plan: ObfuscationPlan, format: ObfuscationFormat): string;
|
|
43
|
+
export {};
|