[GH-ISSUE #6460] [Bug] project-android-application-graph denies an inventoried encrypted root manifest occurrence #3874

Closed
opened 2026-10-31 00:44:50 +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/6460

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

project-android-application-graph lists an encrypted root AndroidManifest.xml in unavailable_components but also emits: "No root AndroidManifest.xml occurrence was observed." The inventory observed the filename; only its content identity is unavailable. Both inspection and projection succeed. Partial coverage and the unavailable component row are correct; this report concerns the contradictory limitation sentence.

Reproduction

From a fresh checkout at the revision below, prepare with npm ci && npm run build:cached. The observed runs used dependencies and a runtime already prepared at that revision. The following commands create the same three fixture variants and use the public CLI; Info-ZIP zip and Node.js are required. fixture-only is a public test password.

WORK="$(mktemp -d)"
mkdir -p "$WORK/home" "$WORK/apk/lib/arm64-v8a"
printf 'manifest bytes\n' > "$WORK/apk/AndroidManifest.xml"
printf 'dex bytes\n' > "$WORK/apk/classes.dex"
printf '\177ELF\002\001\001\000' > "$WORK/apk/lib/arm64-v8a/libentry.so"
(cd "$WORK/apk" && zip -q -P fixture-only ../Locked.apk AndroidManifest.xml)
(cd "$WORK/apk" && zip -q ../Locked.apk classes.dex lib/arm64-v8a/libentry.so)
(cd "$WORK/apk" && zip -q ../Readable.apk AndroidManifest.xml classes.dex lib/arm64-v8a/libentry.so)
(cd "$WORK/apk" && zip -q ../Missing.apk classes.dex lib/arm64-v8a/libentry.so)
for variant in Locked Readable Missing; do
  HOME="$WORK/home" node scripts/rea.mjs inspect-artifact \
    "$WORK/$variant.apk" --json > "$WORK/$variant.inspection.json"
  node -e 'const fs=require("fs"); fs.writeFileSync(process.argv[2], JSON.stringify({inventory_evidence:[JSON.parse(fs.readFileSync(process.argv[1],"utf8"))]}))' \
    "$WORK/$variant.inspection.json" "$WORK/$variant.input.json"
  HOME="$WORK/home" node scripts/rea.mjs project-android-application-graph \
    "$WORK/$variant.input.json" --json > "$WORK/$variant.projection.json"
  node -e 'const r=require(process.argv[1]).normalized_result; console.log(JSON.stringify({variant:process.argv[2],manifests:r.components.manifests,unavailable:r.unavailable_components,coverage:r.coverage,limitations:r.limitations.filter(x=>x.includes("AndroidManifest"))},null,2))' \
    "$WORK/$variant.projection.json" "$variant"
done

The encrypted case reproduced in 2/2 inspection/projection runs; each control was run once. All eight CLI calls exited 0.

Expected behavior

An observed root manifest filename with unavailable contents should receive a content-unavailability qualification, without saying no occurrence was observed. Preserve the null identity, unavailable row and partial coverage. Keep the absence sentence for the genuinely missing-manifest control and omit it for the readable control.

The Android guide says the limitation applies when no root AndroidManifest.xml was observed. The repository invariants require preserving unknown identity rather than treating it as absence. Neither expectation requires decoding the manifest or establishing that the archive is an installable app.

Impact and scope

P3: misleading explanatory wording for an unreadable root manifest. The observed result retains the filename and correctly reports partial coverage, so callers can cross-check unavailable_components. No altered hashes, data loss, target execution or security boundary crossing was observed. Other causes of unreadable members, real-package prevalence and MCP transport were not tested.

REA version

6.7.0 (source 9eb829aca3)

Runtime and platform

Node.js v26.12.1; macOS 27.0.1; arm64. No Android analysis provider or device involved.

Target and artifact context

Synthetic ZIP-format .apk archives with inert manifest/DEX/library data. Only the root manifest in Locked.apk is ZipCrypto encrypted. Projection consumes the complete, unmodified inspect-artifact result as its Evidence carrier.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The encrypted projection returned:

{
  "unavailable_components": [{
    "path": "AndroidManifest.xml",
    "artifact_id": null,
    "sha256": null,
    "format": "file",
    "encrypted": true,
    "limitations": []
  }],
  "coverage": {"status": "partial", "inventory_complete": true}
}

Its limitations simultaneously included:

No root AndroidManifest.xml occurrence was observed. The APK family comes from the .apk suffix of a ZIP archive, so these bytes may not be an APK.

The input inventory itself retained logical_path: "AndroidManifest.xml", encrypted: true and hash_status: "unavailable". The readable control retained the manifest component and omitted the sentence. The missing control had no manifest occurrence and included the sentence. Both controls reported complete-within-inventory with inventory_complete: true.

Typed outcome or recovery expected

Keep a successful partial projection with explicit unavailable content identity. Qualify unread manifest contents separately from whether its filename was observed. The readable and genuinely missing controls should retain their current behavior; these are proposed restoration checks, not a tested fix.

Analysis or suspected cause

Source-derived explanation: androidApplication.ts:99-125 checks all, the resolved file-component list, to decide whether a manifest occurrence was observed. mobileApplicationGraphProjection.ts:141-154 retains unread paths separately as unavailable, and lines 178-184 exclude them from resolved components. The sentence therefore tests content availability while describing path observation.

Additional context

Related closed #2603 fixed encrypted component retention and coverage; both work here. Closed #1491 added the warning for genuinely missing manifests while correcting container classification. Open #5748 concerns Harmony packaging fields. None reports this Android sentence contradicting its retained manifest occurrence.

The existing manifest controls cover readable and absent manifests; the adjacent encrypted-member test encrypts a native library, leaving the encrypted-root-manifest case uncovered. No earlier known-good version or successful restoration has been established.

Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6460 ### 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 `project-android-application-graph` lists an encrypted root `AndroidManifest.xml` in `unavailable_components` but also emits: "No root AndroidManifest.xml occurrence was observed." The inventory observed the filename; only its content identity is unavailable. Both inspection and projection succeed. Partial coverage and the unavailable component row are correct; this report concerns the contradictory limitation sentence. ### Reproduction From a fresh checkout at the revision below, prepare with `npm ci && npm run build:cached`. The observed runs used dependencies and a runtime already prepared at that revision. The following commands create the same three fixture variants and use the public CLI; Info-ZIP `zip` and Node.js are required. `fixture-only` is a public test password. ```sh WORK="$(mktemp -d)" mkdir -p "$WORK/home" "$WORK/apk/lib/arm64-v8a" printf 'manifest bytes\n' > "$WORK/apk/AndroidManifest.xml" printf 'dex bytes\n' > "$WORK/apk/classes.dex" printf '\177ELF\002\001\001\000' > "$WORK/apk/lib/arm64-v8a/libentry.so" (cd "$WORK/apk" && zip -q -P fixture-only ../Locked.apk AndroidManifest.xml) (cd "$WORK/apk" && zip -q ../Locked.apk classes.dex lib/arm64-v8a/libentry.so) (cd "$WORK/apk" && zip -q ../Readable.apk AndroidManifest.xml classes.dex lib/arm64-v8a/libentry.so) (cd "$WORK/apk" && zip -q ../Missing.apk classes.dex lib/arm64-v8a/libentry.so) for variant in Locked Readable Missing; do HOME="$WORK/home" node scripts/rea.mjs inspect-artifact \ "$WORK/$variant.apk" --json > "$WORK/$variant.inspection.json" node -e 'const fs=require("fs"); fs.writeFileSync(process.argv[2], JSON.stringify({inventory_evidence:[JSON.parse(fs.readFileSync(process.argv[1],"utf8"))]}))' \ "$WORK/$variant.inspection.json" "$WORK/$variant.input.json" HOME="$WORK/home" node scripts/rea.mjs project-android-application-graph \ "$WORK/$variant.input.json" --json > "$WORK/$variant.projection.json" node -e 'const r=require(process.argv[1]).normalized_result; console.log(JSON.stringify({variant:process.argv[2],manifests:r.components.manifests,unavailable:r.unavailable_components,coverage:r.coverage,limitations:r.limitations.filter(x=>x.includes("AndroidManifest"))},null,2))' \ "$WORK/$variant.projection.json" "$variant" done ``` The encrypted case reproduced in 2/2 inspection/projection runs; each control was run once. All eight CLI calls exited 0. ### Expected behavior An observed root manifest filename with unavailable contents should receive a content-unavailability qualification, without saying no occurrence was observed. Preserve the null identity, unavailable row and partial coverage. Keep the absence sentence for the genuinely missing-manifest control and omit it for the readable control. [The Android guide](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/docs/android-analysis.md#L227-L234) says the limitation applies when no root `AndroidManifest.xml` was observed. [The repository invariants](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/AGENTS.md#L19-L23) require preserving unknown identity rather than treating it as absence. Neither expectation requires decoding the manifest or establishing that the archive is an installable app. ### Impact and scope P3: misleading explanatory wording for an unreadable root manifest. The observed result retains the filename and correctly reports partial coverage, so callers can cross-check `unavailable_components`. No altered hashes, data loss, target execution or security boundary crossing was observed. Other causes of unreadable members, real-package prevalence and MCP transport were not tested. ### REA version 6.7.0 (source 9eb829aca34be36ff112826337996f6139f82eff) ### Runtime and platform Node.js v26.12.1; macOS 27.0.1; arm64. No Android analysis provider or device involved. ### Target and artifact context Synthetic ZIP-format `.apk` archives with inert manifest/DEX/library data. Only the root manifest in `Locked.apk` is ZipCrypto encrypted. Projection consumes the complete, unmodified `inspect-artifact` result as its Evidence carrier. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence The encrypted projection returned: ```json { "unavailable_components": [{ "path": "AndroidManifest.xml", "artifact_id": null, "sha256": null, "format": "file", "encrypted": true, "limitations": [] }], "coverage": {"status": "partial", "inventory_complete": true} } ``` Its limitations simultaneously included: ```text No root AndroidManifest.xml occurrence was observed. The APK family comes from the .apk suffix of a ZIP archive, so these bytes may not be an APK. ``` The input inventory itself retained `logical_path: "AndroidManifest.xml"`, `encrypted: true` and `hash_status: "unavailable"`. The readable control retained the manifest component and omitted the sentence. The missing control had no manifest occurrence and included the sentence. Both controls reported `complete-within-inventory` with `inventory_complete: true`. ### Typed outcome or recovery expected Keep a successful partial projection with explicit unavailable content identity. Qualify unread manifest contents separately from whether its filename was observed. The readable and genuinely missing controls should retain their current behavior; these are proposed restoration checks, not a tested fix. ### Analysis or suspected cause Source-derived explanation: [androidApplication.ts:99-125](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/src/domain/android/androidApplication.ts#L99-L125) checks `all`, the resolved file-component list, to decide whether a manifest occurrence was observed. [mobileApplicationGraphProjection.ts:141-154](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/src/domain/mobileApplicationGraphProjection.ts#L141-L154) retains unread paths separately as unavailable, and [lines 178-184](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/src/domain/mobileApplicationGraphProjection.ts#L178-L184) exclude them from resolved components. The sentence therefore tests content availability while describing path observation. ### Additional context Related closed [#2603](https://github.com/morluto/rea/issues/2603) fixed encrypted component retention and coverage; both work here. Closed [#1491](https://github.com/morluto/rea/issues/1491) added the warning for genuinely missing manifests while correcting container classification. Open [#5748](https://github.com/morluto/rea/issues/5748) concerns Harmony packaging fields. None reports this Android sentence contradicting its retained manifest occurrence. The existing [manifest controls](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/tests/boundary/filesystem/android/application.test.ts#L178-L205) cover readable and absent manifests; the adjacent encrypted-member test encrypts a native library, leaving the encrypted-root-manifest case uncovered. No earlier known-good version or successful restoration has been established.
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#3874
No description provided.