[GH-ISSUE #6804] [Bug] Apktool 3 version names are truncated or retain producer escapes #4155

Closed
opened 2026-10-31 00:47:14 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6804

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

The imported production parseApktoolYml truncates a version name containing # and preserves producer escape sequences instead of the original backslash or emoji when given synthetic metadata matching Apktool 3.0.3's writer. 1.0 #beta becomes 1.0; a single backslash becomes two; 1.0 🚀 becomes the literal characters 1.0 \uD83D\uDE80. The plain and standard quoted-YAML controls retain their values.

This is an executed source-parser reproduction. No Apktool engine, APK decode, public CLI, MCP session, device, or target application was executed. The connection to Apktool's actual output format is source-derived from its released writer below.

Reproduction

With Node.js v24.22.1 on PATH and locked dependencies installed, run from the source checkout:

node --version
node node_modules/.bin/tsx --eval '
import { parseApktoolYml } from "./src/apktool/ApktoolDecodeOutput.ts";
const cases = [
  ["plain", "1.0.0", "1.0.0"],
  ["hash", "1.0 #beta", "1.0 #beta"],
  ["backslash", String.raw`1.0\beta`, String.raw`1.0\\beta`],
  ["emoji", "1.0 🚀", String.raw`1.0 \uD83D\uDE80`],
  ["quoted-control", "1.0 #beta", "\"1.0 #beta\""],
];
for (const [name, expected, source] of cases) {
  const yml = `version: 3.0.3\nversionInfo:\n  versionCode: 7\n  versionName: ${source}\n`;
  const actual = parseApktoolYml(yml).versionName;
  console.log(JSON.stringify({ name, expected, actual, matches: actual === expected }));
}
'

Executed once in this reduced form; an earlier five-case probe with complete SDK/version mappings observed the same three mismatches before drafting.

Expected behavior

Preserve the version name represented by the selected decoder's metadata dialect, while continuing to interpret standard quoted YAML correctly. REA advertises Apktool 3.0.3 compatibility and projects version metadata from apktool.yml.

In Apktool's released 3.0.3 source, VersionInfo.write calls YamlWriter.writeString without quoting. Its escape owner doubles backslashes and escapes supplementary Unicode as surrogate pairs, while leaving # unchanged. Its reader unescapes the value instead of applying general YAML comment rules. The failing fixtures follow that producer dialect; they are not arbitrary malformed XML or invalid version codes.

Impact and scope

P2: bounded metadata corruption for particular version-name characters. All three affected inputs returned successfully with incorrect versionName; other version/SDK facts in the earlier probe remained intact. The production provider assigns this value directly to metadata.version_name, so publication through that provider is source-derived, not an executed end-to-end claim. Real APK prevalence and behavior with other decoder releases are unknown.

REA version

rea-agents 6.7.0; source revision 60accad2de8f2862fe91901502fe26cc5390fb36. Imported source with locked dependencies; no separately installed package tested.

Runtime and platform

Node.js v24.22.1; Linux x64. No Java or Apktool engine used. Producer-format references are pinned to Apktool v3.0.3, commit 18b5e99cb56ff9451e8aa55b065dcf5bbd616975.

Target and artifact context

Five self-authored apktool.yml strings: one plain control, three producer-shaped version-name cases, and one standard quoted-YAML control. No APK or private target data.

Interface

Other

Earliest failure stage

Transport or result projection

Relevant output or evidence

Actual reduced-probe stdout:

v24.22.1
{"name":"plain","expected":"1.0.0","actual":"1.0.0","matches":true}
{"name":"hash","expected":"1.0 #beta","actual":"1.0","matches":false}
{"name":"backslash","expected":"1.0\\beta","actual":"1.0\\\\beta","matches":false}
{"name":"emoji","expected":"1.0 🚀","actual":"1.0 \\uD83D\\uDE80","matches":false}
{"name":"quoted-control","expected":"1.0 #beta","actual":"1.0 #beta","matches":true}

Evidence provenance: observed parser output plus source-derived producer compatibility. Local verification: reproduced. Reproduction completeness: complete for the imported parser; real decoder and adapter behavior were not exercised.

Typed outcome or recovery expected

Return the original represented version name, or explicitly classify an unsupported producer dialect; do not return a silently altered version identity as parsed metadata.

Analysis or suspected cause

Source-derived: parseApktoolYml applies YAML core semantics, which discard the hash suffix as a comment. yamlScalar returns the resulting plain scalar without interpreting Apktool 3's producer escapes. These are two manifestations of the same decoder-dialect mismatch.

Additional context

Open issue bodies were screened locally; GitHub issue searches covered open and closed versionName, version_name apktool, apktool metadata, YamlWriter, and apktool escape. No matching report was found. Related #4882 concerns retaining completed metadata after subsequent read failures; #4694 concerns valid XML replacement characters. Fresh open/closed searches and a canonical recent-open-issue listing immediately before filing found no matching report.

NOT_ENOUGH_INFO: last known-good release, first affected release, real APK occurrence rate, and an independently exercised workaround. No source fix or restoration is claimed. Prepared with AI assistance.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Three released-producer-shaped metadata fixtures return incorrect version names through the production parser; this is a bounded correctness defect in package metadata, with unaffected controls.
  • Workaround status: unverified
  • Workaround basis: Reading the decoded manifest or inspecting Apktool metadata outside REA may recover the original version name, but neither route was exercised against a real APK in this investigation.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6804 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior The imported production `parseApktoolYml` truncates a version name containing ` #` and preserves producer escape sequences instead of the original backslash or emoji when given synthetic metadata matching Apktool 3.0.3's writer. `1.0 #beta` becomes `1.0`; a single backslash becomes two; `1.0 🚀` becomes the literal characters `1.0 \uD83D\uDE80`. The plain and standard quoted-YAML controls retain their values. This is an executed source-parser reproduction. No Apktool engine, APK decode, public CLI, MCP session, device, or target application was executed. The connection to Apktool's actual output format is source-derived from its released writer below. ### Reproduction With Node.js v24.22.1 on PATH and locked dependencies installed, run from the source checkout: ```sh node --version node node_modules/.bin/tsx --eval ' import { parseApktoolYml } from "./src/apktool/ApktoolDecodeOutput.ts"; const cases = [ ["plain", "1.0.0", "1.0.0"], ["hash", "1.0 #beta", "1.0 #beta"], ["backslash", String.raw`1.0\beta`, String.raw`1.0\\beta`], ["emoji", "1.0 🚀", String.raw`1.0 \uD83D\uDE80`], ["quoted-control", "1.0 #beta", "\"1.0 #beta\""], ]; for (const [name, expected, source] of cases) { const yml = `version: 3.0.3\nversionInfo:\n versionCode: 7\n versionName: ${source}\n`; const actual = parseApktoolYml(yml).versionName; console.log(JSON.stringify({ name, expected, actual, matches: actual === expected })); } ' ``` Executed once in this reduced form; an earlier five-case probe with complete SDK/version mappings observed the same three mismatches before drafting. ### Expected behavior Preserve the version name represented by the selected decoder's metadata dialect, while continuing to interpret standard quoted YAML correctly. REA [advertises Apktool 3.0.3 compatibility](https://github.com/morluto/rea/blob/60accad2de8f2862fe91901502fe26cc5390fb36/docs/apktool-resource-analysis.md#L9-L12) and [projects version metadata from apktool.yml](https://github.com/morluto/rea/blob/60accad2de8f2862fe91901502fe26cc5390fb36/docs/apktool-resource-analysis.md#L90-L98). In Apktool's released 3.0.3 source, [`VersionInfo.write`](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/VersionInfo.java#L52-L59) calls [`YamlWriter.writeString`](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlWriter.java#L67-L87) without quoting. Its [escape owner](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlStringEscapeUtils.java#L65-L118) doubles backslashes and escapes supplementary Unicode as surrogate pairs, while leaving ` #` unchanged. Its [reader](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlLine.java#L103-L116) unescapes the value instead of applying general YAML comment rules. The failing fixtures follow that producer dialect; they are not arbitrary malformed XML or invalid version codes. ### Impact and scope P2: bounded metadata corruption for particular version-name characters. All three affected inputs returned successfully with incorrect `versionName`; other version/SDK facts in the earlier probe remained intact. The production provider [assigns this value directly to metadata.version_name](https://github.com/morluto/rea/blob/60accad2de8f2862fe91901502fe26cc5390fb36/src/apktool/ApktoolProvider.ts#L610-L615), so publication through that provider is source-derived, not an executed end-to-end claim. Real APK prevalence and behavior with other decoder releases are unknown. ### REA version `rea-agents` 6.7.0; source revision `60accad2de8f2862fe91901502fe26cc5390fb36`. Imported source with locked dependencies; no separately installed package tested. ### Runtime and platform Node.js v24.22.1; Linux x64. No Java or Apktool engine used. Producer-format references are pinned to Apktool v3.0.3, commit `18b5e99cb56ff9451e8aa55b065dcf5bbd616975`. ### Target and artifact context Five self-authored apktool.yml strings: one plain control, three producer-shaped version-name cases, and one standard quoted-YAML control. No APK or private target data. ### Interface Other ### Earliest failure stage Transport or result projection ### Relevant output or evidence Actual reduced-probe stdout: ```text v24.22.1 {"name":"plain","expected":"1.0.0","actual":"1.0.0","matches":true} {"name":"hash","expected":"1.0 #beta","actual":"1.0","matches":false} {"name":"backslash","expected":"1.0\\beta","actual":"1.0\\\\beta","matches":false} {"name":"emoji","expected":"1.0 🚀","actual":"1.0 \\uD83D\\uDE80","matches":false} {"name":"quoted-control","expected":"1.0 #beta","actual":"1.0 #beta","matches":true} ``` Evidence provenance: observed parser output plus source-derived producer compatibility. Local verification: reproduced. Reproduction completeness: complete for the imported parser; real decoder and adapter behavior were not exercised. ### Typed outcome or recovery expected Return the original represented version name, or explicitly classify an unsupported producer dialect; do not return a silently altered version identity as parsed metadata. ### Analysis or suspected cause Source-derived: [`parseApktoolYml`](https://github.com/morluto/rea/blob/60accad2de8f2862fe91901502fe26cc5390fb36/src/apktool/ApktoolDecodeOutput.ts#L25-L51) applies YAML core semantics, which discard the hash suffix as a comment. [`yamlScalar`](https://github.com/morluto/rea/blob/60accad2de8f2862fe91901502fe26cc5390fb36/src/apktool/ApktoolDecodeOutput.ts#L164-L175) returns the resulting plain scalar without interpreting Apktool 3's producer escapes. These are two manifestations of the same decoder-dialect mismatch. ### Additional context Open issue bodies were screened locally; GitHub issue searches covered open and closed `versionName`, `version_name apktool`, `apktool metadata`, `YamlWriter`, and `apktool escape`. No matching report was found. Related #4882 concerns retaining completed metadata after subsequent read failures; #4694 concerns valid XML replacement characters. Fresh open/closed searches and a canonical recent-open-issue listing immediately before filing found no matching report. NOT_ENOUGH_INFO: last known-good release, first affected release, real APK occurrence rate, and an independently exercised workaround. No source fix or restoration is claimed. Prepared with AI assistance. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Three released-producer-shaped metadata fixtures return incorrect version names through the production parser; this is a bounded correctness defect in package metadata, with unaffected controls. - Workaround status: unverified - Workaround basis: Reading the decoded manifest or inspecting Apktool metadata outside REA may recover the original version name, but neither route was exercised against a real APK in this investigation.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4155
No description provided.