fix(apktool): read Apktool 2.9+ metadata in its line-oriented dialect (#7747)
Apktool 2.9.0 replaced SnakeYAML with the YamlWriter that 3.x still uses: apktool.yml starts with an untagged `version: 2.x.y` line and strings are written unquoted with Java-style escapes. Only a `version: 3.x.y` header selected the producer dialect, so 2.9.0-2.12.x output went through YAML core parsing: a versionName of `1.0 #beta` became `1.0`, escaped quotes kept their backslashes, an empty name became null, and `1.0: beta` made the whole decode fail as invalid metadata. Select the line-oriented reader for 2.9.0 and later as well. Older tagged SnakeYAML documents keep YAML scalar semantics.
This commit is contained in:
1 parent
fea4f2e397
commit
33698e6b31
3 files changed
+65
-12
No files matched your search
@@ -100,12 +100,13 @@ still suppresses all string projection, including in this mode.
|
||||
- **Metadata comes from apktool.yml as apktool printed it.** Version codes
|
||||
keep their own spelling (`'7'` versus `7`); `package_name` is read from
|
||||
the decoded manifest, not inferred.
|
||||
Apktool 3's emitted `version: 3.x.y` header selects its single-line string
|
||||
dialect: literal hashes, single quotes, empty strings, and the text `null`
|
||||
stay part of the value; producer backslashes and Unicode escapes decode
|
||||
once. Older/tagged documents retain YAML scalar semantics; double-quoted
|
||||
values also retain YAML string semantics.
|
||||
The `version: 2.9.0`-or-later header that Apktool 2.9+ and 3 emit selects
|
||||
their single-line string dialect: literal hashes, single quotes, empty
|
||||
strings, and the text `null` stay part of the value; producer backslashes
|
||||
and Unicode escapes decode once. Older/tagged documents retain YAML scalar
|
||||
semantics; double-quoted values also retain YAML string semantics.
|
||||
This distinction follows the released
|
||||
[2.9.0 metadata writer](https://github.com/iBotPeaches/Apktool/blob/b64e257c9b689eb9630ad0241f2fe4f4a749955b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/VersionInfo.java#L38-L42),
|
||||
[3.0.3 metadata writer](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/VersionInfo.java#L49-L55)
|
||||
and [string escape owner](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlStringEscapeUtils.java#L55-L116).
|
||||
- **The manifest is the decoded text**, bounded at 512 KiB and reported as
|
||||
|
||||
@@ -190,6 +190,52 @@ sdkInfo:
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseApktoolYml Apktool 2.9+ metadata dialect", () => {
|
||||
// Source-derived from ApkInfo.write, VersionInfo.write and YamlWriter at
|
||||
// Apktool v2.9.0 and v2.12.1, whose string escaping matches 3.0.3.
|
||||
const yml = (version: string, versionName: string) =>
|
||||
[
|
||||
`version: ${version}`,
|
||||
"apkFileName: app-release.apk",
|
||||
"isFrameworkApk: false",
|
||||
"usesFramework:",
|
||||
" ids:",
|
||||
" - 1",
|
||||
" tag: null",
|
||||
"sdkInfo:",
|
||||
" minSdkVersion: 24",
|
||||
" targetSdkVersion: 34",
|
||||
"packageInfo:",
|
||||
" forcedPackageId: 127",
|
||||
" renameManifestPackage: null",
|
||||
"versionInfo:",
|
||||
" versionCode: 7",
|
||||
` versionName: ${versionName}`,
|
||||
"sharedLibrary: false",
|
||||
"sparseResources: false",
|
||||
"doNotCompress:",
|
||||
"- arsc",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
it.each([
|
||||
["2.9.0", "1.0 #beta", "1.0 #beta"],
|
||||
["2.9.3", String.raw`release \"quoted\"`, 'release "quoted"'],
|
||||
["2.12.1", "1.0: beta", "1.0: beta"],
|
||||
["2.12.1", "", ""],
|
||||
])(
|
||||
"reads Apktool %s version string %j as its writer escaped it",
|
||||
(version, source, expected) => {
|
||||
expect(parseApktoolYml(yml(version, source))).toEqual({
|
||||
versionName: expected,
|
||||
versionCode: "7",
|
||||
minSdkVersion: "24",
|
||||
targetSdkVersion: "34",
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("parseManifestPackage", () => {
|
||||
it("reads the package attribute from a decoded manifest", () => {
|
||||
const manifest =
|
||||
|
||||
@@ -23,14 +23,19 @@ const metaInfoTag = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the producer's metadata dialect once. Apktool 3 emits an untagged,
|
||||
* line-oriented document with Java-escaped strings; older tagged documents
|
||||
* use YAML core semantics. Numeric values retain their source spelling.
|
||||
* Read the producer's metadata dialect once. Apktool 2.9.0 and later (including
|
||||
* 3) emit an untagged, line-oriented document with Java-escaped strings; older
|
||||
* tagged documents use YAML core semantics. Numeric values retain their source
|
||||
* spelling.
|
||||
*/
|
||||
export const parseApktoolYml = (yml: string): ApktoolMetadata => {
|
||||
const source = yml.replace(/^\uFEFF/u, "");
|
||||
const header = source.split(/\r\n?|\n/u, 1)[0] ?? "";
|
||||
if (/^version: 3\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?$/u.test(header))
|
||||
if (
|
||||
/^version: (?:2\.(?:9|[1-9]\d)|3\.\d+)\.\d+(?:[-+][0-9A-Za-z.+-]+)?$/u.test(
|
||||
header,
|
||||
)
|
||||
)
|
||||
return parseApktool3Metadata(source);
|
||||
const document = parseDocument(yml, {
|
||||
schema: "core",
|
||||
@@ -65,9 +70,10 @@ const apktool3Fields = new Map<string, keyof ApktoolMetadata>([
|
||||
]);
|
||||
|
||||
/**
|
||||
* Apktool 3.0.3 YamlWriter writes strings without YAML quoting or comment
|
||||
* syntax. Read its direct mapping records before a YAML parser can discard
|
||||
* hashes, consume quotes, or reinterpret an empty string or literal `null`.
|
||||
* Apktool 3.0.3 YamlWriter, like the 2.9.0 writer it descends from, writes
|
||||
* strings without YAML quoting or comment syntax. Read its direct mapping
|
||||
* records before a YAML parser can discard hashes, consume quotes, or
|
||||
* reinterpret an empty string or literal `null`.
|
||||
*/
|
||||
const parseApktool3Metadata = (source: string): ApktoolMetadata => {
|
||||
const values = new Map<keyof ApktoolMetadata, string>();
|
||||
|
||||
Reference in new issue
Block a user