[GH-ISSUE #5413] [Bug] Harmony application graph omits inventoried RSA, DSA, and EC signing entries #3280

Closed
opened 2026-10-31 00:38:44 +00:00 by bsakaguchi · 1 comment
Owner

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

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 public CLI project-harmony-application-graph omits inventoried META-INF/CERT.RSA, META-INF/CERT.DSA, and META-INF/CERT.EC paths from components.signing. A .p7b member with identical opaque bytes is included. The preceding inspect-artifact result retains all four paths with authenticated artifact identities and verified hashes.

This is a path-classification report. The synthetic inputs do not establish valid signatures or a cryptographically signed package. The independent verifier ran each of the three cases twice; every inspection and projection exited 0 with the same result.

Reproduction

Use the source revision below with locked dependencies installed and the compiled runtime built (npm ci, then npm run build:cached are clean-checkout prerequisites; the verifier used an already prepared runtime). From the repository root, run this self-contained script. It creates disposable ZIP_STORED HAP fixtures, isolates HOME and XDG directories, and invokes the compiled public CLI without an SDK, provider, or network access:

python3 - <<'PYTHON'
import json, os, subprocess, tempfile, zipfile
from pathlib import Path

work = Path(tempfile.mkdtemp(prefix="rea-signing-"))
env = os.environ.copy()
for key, directory in [("HOME", "home"), ("XDG_CONFIG_HOME", "config"),
                       ("XDG_DATA_HOME", "data"), ("XDG_CACHE_HOME", "cache"),
                       ("XDG_STATE_HOME", "state")]:
    path = work / directory
    path.mkdir()
    env[key] = str(path)

def cli(*args):
    result = subprocess.run(["node", "scripts/rea.mjs", *args, "--format", "json"],
                            env=env, check=True, capture_output=True, text=True)
    return json.loads(result.stdout)

def occurrences(value):
    if isinstance(value, dict):
        if "logical_path" in value:
            yield value
        for child in value.values():
            yield from occurrences(child)
    elif isinstance(value, list):
        for child in value:
            yield from occurrences(child)

for label, suffixes in [("original", ["RSA"]),
                        ("control", ["RSA", "DSA", "EC", "p7b"]),
                        ("empty", [])]:
    package = work / (label + ".hap")
    with zipfile.ZipFile(package, "w", zipfile.ZIP_STORED) as archive:
        archive.writestr("module.json", '{"module":{"name":"entry"}}')
        archive.writestr("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
        archive.writestr("META-INF/CERT.SF", "Signature-Version: 1.0\n")
        for suffix in suffixes:
            archive.writestr("META-INF/CERT." + suffix, b"pkcs7")
    inventory = cli("inspect-artifact", str(package))
    request = work / (label + ".input.json")
    request.write_text(json.dumps({"inventory_evidence": [inventory]}))
    result = cli("project-harmony-application-graph", str(request))["normalized_result"]
    retained = sorted({row["logical_path"] for row in occurrences(inventory)
                       if row["logical_path"].startswith("META-INF/")})
    print(json.dumps({"case": label, "inventoried": retained,
                      "signing": [row["path"] for row in result["components"]["signing"]],
                      "coverage": result["coverage"],
                      "unavailable_components": result["unavailable_components"]}))
PYTHON

The original case inventories RSA; the control adds DSA, EC, and p7b with identical payloads; the empty control contains only manifest/signature-file names. The script leaves its generated fixtures in its disposable directory.

Expected behavior

The signing component list should retain the RSA, DSA, and EC certificate-block paths and their inventoried identities, alongside p7b in the control, without interpreting the opaque payloads as valid certificates or claiming signature verification. The manifest and signature-file members should remain outside this certificate-entry list; the empty control should remain empty.

Source-derived contract: docs/harmony-analysis.md:34-38 documents exact component paths, artifact IDs, and hashes including signing; lines 54-59 limit this to path presence and exclude signing verification. The returned limitation at src/domain/harmony/harmonyApplication.ts:110 describes META-INF certificate-entry suffixes. The Android classifier at src/domain/android/androidApplication.ts:170-172 recognizes RSA, DSA, and EC, supporting the naming convention rather than proving real HarmonyOS signing-tool behavior.

Impact and scope

P2: callers receive an incomplete signing component list for a bounded suffix class. The original archive and inventory remain intact. A demonstrated workaround is to recover the paths, authenticated artifact IDs, and verified hashes from the inspection inventory. Real-world prevalence, cryptographically signed HAP behavior, and MCP execution were not tested. No signature-trust or execution boundary was crossed.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8.

Runtime and platform

Node.js v26.12.0; macOS (darwin), arm64. No external analysis provider or signing tool was involved.

Target and artifact context

Locally generated .hap ZIP_STORED archives with module.json, META-INF/MANIFEST.MF, META-INF/CERT.SF, and the certificate-block candidate paths shown above. Certificate-block contents are the placeholder bytes pkcs7. Projection input wraps the actual inspection result in inventory_evidence.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The independently executed CLI comparison returned:

{"case":"original","signing":[]}
{"case":"control","signing":["META-INF/CERT.p7b"]}
{"case":"empty","signing":[]}

Inspection retained META-INF/CERT.RSA in the original case and RSA, DSA, EC, and p7b in the control. All three projections returned coverage.status: "complete-within-inventory", coverage.inventory_complete: true, and unavailable_components: []. Complete inventory coverage is context for the classification omission, not a signature-verification guarantee. The existing warning that an empty signing list does not imply an unsigned package was present.

Typed outcome or recovery expected

normalized_result.components.signing should include META-INF/CERT.RSA in the original case and the RSA, DSA, EC, and p7b paths in the control with the artifact IDs and hashes retained by inspection. Preserve the empty control and the existing warning about signing data outside archive entries.

Analysis or suspected cause

Source-derived reasoning: src/domain/harmony/harmonyApplication.ts:193-195 includes p7b, sig, cer, crt, pem, and provision suffixes, but excludes RSA, DSA, and EC. Incomplete suffix classification best explains the executed identical-payload p7b control and the documented path-presence contract. Invalid signature bytes or a semantic-verification exclusion do not explain that contrast within this execution-free classifier.

Additional context

Prepared with AI assistance and independently reproduced through the compiled CLI. No cryptographic verification or real signed-package claim is made.

Before submission, fetched upstream main at e84468c0bb0476412ac0c8161f7c2ecc9a6864ca: no post-baseline commits touched the cited Harmony documentation, Harmony classifier, or Android classifier, and the upstream Harmony suffix filter remains unchanged. Searched open/closed issues and PRs using the command name, signing/certificate symptoms, and harmonyApplication. Related #4489 concerns pkgContextInfo declarations and other uncategorized entries; this report concerns omitted certificate-block suffixes in the existing signing category.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5413 ### 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 public CLI `project-harmony-application-graph` omits inventoried `META-INF/CERT.RSA`, `META-INF/CERT.DSA`, and `META-INF/CERT.EC` paths from `components.signing`. A `.p7b` member with identical opaque bytes is included. The preceding `inspect-artifact` result retains all four paths with authenticated artifact identities and verified hashes. This is a path-classification report. The synthetic inputs do not establish valid signatures or a cryptographically signed package. The independent verifier ran each of the three cases twice; every inspection and projection exited 0 with the same result. ### Reproduction Use the source revision below with locked dependencies installed and the compiled runtime built (`npm ci`, then `npm run build:cached` are clean-checkout prerequisites; the verifier used an already prepared runtime). From the repository root, run this self-contained script. It creates disposable ZIP_STORED HAP fixtures, isolates HOME and XDG directories, and invokes the compiled public CLI without an SDK, provider, or network access: ```sh python3 - <<'PYTHON' import json, os, subprocess, tempfile, zipfile from pathlib import Path work = Path(tempfile.mkdtemp(prefix="rea-signing-")) env = os.environ.copy() for key, directory in [("HOME", "home"), ("XDG_CONFIG_HOME", "config"), ("XDG_DATA_HOME", "data"), ("XDG_CACHE_HOME", "cache"), ("XDG_STATE_HOME", "state")]: path = work / directory path.mkdir() env[key] = str(path) def cli(*args): result = subprocess.run(["node", "scripts/rea.mjs", *args, "--format", "json"], env=env, check=True, capture_output=True, text=True) return json.loads(result.stdout) def occurrences(value): if isinstance(value, dict): if "logical_path" in value: yield value for child in value.values(): yield from occurrences(child) elif isinstance(value, list): for child in value: yield from occurrences(child) for label, suffixes in [("original", ["RSA"]), ("control", ["RSA", "DSA", "EC", "p7b"]), ("empty", [])]: package = work / (label + ".hap") with zipfile.ZipFile(package, "w", zipfile.ZIP_STORED) as archive: archive.writestr("module.json", '{"module":{"name":"entry"}}') archive.writestr("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n") archive.writestr("META-INF/CERT.SF", "Signature-Version: 1.0\n") for suffix in suffixes: archive.writestr("META-INF/CERT." + suffix, b"pkcs7") inventory = cli("inspect-artifact", str(package)) request = work / (label + ".input.json") request.write_text(json.dumps({"inventory_evidence": [inventory]})) result = cli("project-harmony-application-graph", str(request))["normalized_result"] retained = sorted({row["logical_path"] for row in occurrences(inventory) if row["logical_path"].startswith("META-INF/")}) print(json.dumps({"case": label, "inventoried": retained, "signing": [row["path"] for row in result["components"]["signing"]], "coverage": result["coverage"], "unavailable_components": result["unavailable_components"]})) PYTHON ``` The original case inventories RSA; the control adds DSA, EC, and p7b with identical payloads; the empty control contains only manifest/signature-file names. The script leaves its generated fixtures in its disposable directory. ### Expected behavior The signing component list should retain the RSA, DSA, and EC certificate-block paths and their inventoried identities, alongside p7b in the control, without interpreting the opaque payloads as valid certificates or claiming signature verification. The manifest and signature-file members should remain outside this certificate-entry list; the empty control should remain empty. Source-derived contract: `docs/harmony-analysis.md:34-38` documents exact component paths, artifact IDs, and hashes including signing; lines 54-59 limit this to path presence and exclude signing verification. The returned limitation at `src/domain/harmony/harmonyApplication.ts:110` describes META-INF certificate-entry suffixes. The Android classifier at `src/domain/android/androidApplication.ts:170-172` recognizes RSA, DSA, and EC, supporting the naming convention rather than proving real HarmonyOS signing-tool behavior. ### Impact and scope P2: callers receive an incomplete signing component list for a bounded suffix class. The original archive and inventory remain intact. A demonstrated workaround is to recover the paths, authenticated artifact IDs, and verified hashes from the inspection inventory. Real-world prevalence, cryptographically signed HAP behavior, and MCP execution were not tested. No signature-trust or execution boundary was crossed. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. ### Runtime and platform Node.js v26.12.0; macOS (darwin), arm64. No external analysis provider or signing tool was involved. ### Target and artifact context Locally generated `.hap` ZIP_STORED archives with `module.json`, `META-INF/MANIFEST.MF`, `META-INF/CERT.SF`, and the certificate-block candidate paths shown above. Certificate-block contents are the placeholder bytes `pkcs7`. Projection input wraps the actual inspection result in `inventory_evidence`. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence The independently executed CLI comparison returned: ```json {"case":"original","signing":[]} {"case":"control","signing":["META-INF/CERT.p7b"]} {"case":"empty","signing":[]} ``` Inspection retained `META-INF/CERT.RSA` in the original case and RSA, DSA, EC, and p7b in the control. All three projections returned `coverage.status: "complete-within-inventory"`, `coverage.inventory_complete: true`, and `unavailable_components: []`. Complete inventory coverage is context for the classification omission, not a signature-verification guarantee. The existing warning that an empty signing list does not imply an unsigned package was present. ### Typed outcome or recovery expected `normalized_result.components.signing` should include `META-INF/CERT.RSA` in the original case and the RSA, DSA, EC, and p7b paths in the control with the artifact IDs and hashes retained by inspection. Preserve the empty control and the existing warning about signing data outside archive entries. ### Analysis or suspected cause Source-derived reasoning: `src/domain/harmony/harmonyApplication.ts:193-195` includes p7b, sig, cer, crt, pem, and provision suffixes, but excludes RSA, DSA, and EC. Incomplete suffix classification best explains the executed identical-payload p7b control and the documented path-presence contract. Invalid signature bytes or a semantic-verification exclusion do not explain that contrast within this execution-free classifier. ### Additional context Prepared with AI assistance and independently reproduced through the compiled CLI. No cryptographic verification or real signed-package claim is made. Before submission, fetched upstream main at `e84468c0bb0476412ac0c8161f7c2ecc9a6864ca`: no post-baseline commits touched the cited Harmony documentation, Harmony classifier, or Android classifier, and the upstream Harmony suffix filter remains unchanged. Searched open/closed issues and PRs using the command name, signing/certificate symptoms, and `harmonyApplication`. Related [#4489](https://github.com/morluto/rea/issues/4489) concerns pkgContextInfo declarations and other uncategorized entries; this report concerns omitted certificate-block suffixes in the existing signing category.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

Signing regex now includes rsa,dsa,ec case-insensitively. Probe retained exactly RSA/DSA/EC/p7b paths and excluded MANIFEST.MF/CERT.SF; archive fixture files were removed afterward. Dedicated regression asserts those exact names from opaque bytes and retained inventory.

Implementation: harmonyApplication.ts.

The relevant regression checks in application.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380740280 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. Signing regex now includes rsa,dsa,ec case-insensitively. Probe retained exactly RSA/DSA/EC/p7b paths and excluded MANIFEST.MF/CERT.SF; archive fixture files were removed afterward. Dedicated regression asserts those exact names from opaque bytes and retained inventory. Implementation: [harmonyApplication.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/harmony/harmonyApplication.ts#L213). The relevant regression checks in [application.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/filesystem/harmony/application.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#3280
No description provided.