Skip to content

fix(apktool): preserve valid replacement characters in decoded XML - #4697

Merged
morluto merged 1 commit into
morluto:mainfrom
ys0v9:fix/apktool-valid-replacement-character
Oct 17, 2026
Merged

morluto merged 1 commit into
morluto:mainfrom
ys0v9:fix/apktool-valid-replacement-character

Conversation

@ys0v9

@ys0v9 ys0v9 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #4694. Preserve literal U+FFFD (�) in Apktool-decoded XML so one valid string no longer causes the entire string table to be omitted. The same parser also preserves manifest package extraction when another attribute contains U+FFFD.

Problem and expected behavior

For <resources><string name="good">Hello</string><string name="edge">�</string></resources>, REA returned no strings and coverage: "partial", although real Apktool decoded both entries. U+FFFD is valid XML and these UTF-8 bytes are valid. Both strings should be returned verbatim with complete coverage for this fixture.

Change and scope

At the shared Apktool XML parsing boundary, ignore only xmldom's U+FFFD warning. Keep all other diagnostics as parse failures. The existing fatal UTF-8 decoder continues to reject invalid source bytes. Both consumers use this boundary; no downstream repair or new abstraction is needed.

This is an ordinary fix within the existing resource-analysis boundary. A maintainer scope decision is not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: ApktoolDecodeOutput.parseXml, decoded XML projection.
  • Contribution scope fit and linked maintainer decision: existing static resource analysis; not applicable.
  • CLI and MCP/tool-catalog contract: same requests and result shapes; valid XML now retains its strings/package facts.
  • Provider, bridge, target-format, or platform compatibility: same Apktool launcher and decoded XML format; no new version requirement.
  • Evidence, artifact, provenance, or reconstruction contract: preserve source values and unaffected siblings; malformed-input outcomes remain visible.
  • Target selection, authority, upstream behavior, and cleanup: unchanged. Existing workflow reads the selected APK, launches Apktool, and writes an owned temporary workspace. It does not install or execute the target APK.
  • Generated metadata, package, or installation impact: none; no schema, dependency, or generated-file changes.

Expected effects of the existing workflow:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Parser regressions cover U+FFFD in strings and manifest attributes, unaffected siblings, unquoted attributes, and mismatched tags. Provider regressions cover complete projection and refusal of invalid UTF-8 bytes.
  • On base 5717ad24aa6471aa4207c370e26274866fb8aee5, the new tests produced 3 failures / 46 passes. With this fix, the same selection produced 49 passes.
  • Real-provider evidence used two synthetic AAPT2-built APKs, independently decoded by Apktool 3.0.3 and analyzed through node scripts/rea.mjs decode-android-resources <apk> --json. Each fixture was run twice before and twice after the fix; all CLI exits were 0.
Fixture Before After
edge=plain, good=Hello Both strings; complete Both strings; complete
edge=�, good=Hello strings: []; partial Both strings, including literal �; complete

The prior String entries omitted due to malformed XML or unsupported markup: 1 limitation is absent after the fix. The same failing APK SHA-256 was used before and after: 7a43b05965c43e05360cc47bfbac3dc14d606428e7ab34def0188a3a338453c2.

Remaining proof gaps: real-tool execution covered macOS arm64 and Apktool 3.0.3 only. The manifest U+FFFD case is covered by parser and simulated-provider tests, not a separate real APK. MCP and other OS/provider versions were not exercised.

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- src/apktool/ApktoolDecodeOutput.test.ts src/apktool/ApktoolProvider.test.ts — 49 passed.
  • npm run check — passed: typecheck, format, lint, Knip, metadata, module boundaries, and architecture guards. Lint reported 0 errors and 1,615 warnings across the repository.
  • npm run build:cached — passed.
  • Public CLI with real Apktool — both fixtures passed both post-fix runs, preserving two strings and complete coverage.
  • git diff --check — passed.

Toolchain: Node.js 24.18.0, npm 11.16.0, OpenJDK 21.0.10, Apktool 3.0.3, macOS 26.5.1 arm64. The complete test suite remains for CI; generated-document checks are not applicable because no contract or generated metadata changed.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable to those providers; real macOS Apktool coverage described above.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new authority or target execution; invalid UTF-8 and malformed XML remain rejected. Test APKs were synthetic and never installed or executed.

Author readiness

Human owner: @ys0v9

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

@ys0v9
ys0v9 marked this pull request as ready for review October 17, 2026 06:48
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-17T06:48:52.983560Z 6be293f Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@greptile-apps

greptile-apps Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issue was found.

Summary

parseXml now ignores the warning for literal U+FFFD while retaining rejection of other XML diagnostics.

  • Decoded XML keeps valid replacement characters in strings and manifests.

Reviews (1) · Last reviewed commit: "fix(apktool): preserve valid replacement..." · Reviewed by Greptile

@morluto
morluto merged commit 326c073 into morluto:main Oct 17, 2026
1 check passed
@morluto

morluto commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Thanks for spotting that a literal replacement character is valid XML, @ys0v9. Ignoring only that xmldom warning keeps good Apktool strings and package names available while other parse errors still surface. Glad to welcome you to REA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Apktool drops the entire string table when valid XML contains U+FFFD

3 participants