[GH-ISSUE #6116] [Bug] Unsearchable selected JavaScript directory becomes generic artifact I/O failure #3750

Closed
opened 2026-10-31 00:43:38 +00:00 by bsakaguchi · 4 comments
Owner

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

Originally assigned to: @kaoru0822-kitauji on GitHub.

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 built analyze-javascript-application command reports a selected directory with read permission but no search permission as artifact_operation_failed / execution_failure, reason io. The result has no structured path, system_code or filesystem-read boundary. The actual EACCES and child path survive in details.detail as prose.

On this host, the directory can be opened/listed; lstat on its index.js entry fails because the selected root lacks search permission. The same directory succeeds with mode 0700 before and after the failing mode-0400 call.

Reproduction

As a non-root POSIX user, create a caller-selected directory $app containing index.js with export const x=1;. From a prepared built checkout with pinned Node.js, run:

chmod 0700 "$app"
node scripts/rea.mjs analyze-javascript-application "$app" --json
chmod 0400 "$app"
node scripts/rea.mjs analyze-javascript-application "$app" --json
chmod 0700 "$app"
node scripts/rea.mjs analyze-javascript-application "$app" --json

This sequence was executed with isolated configuration/state. Exit statuses were 0, 1 and 0. Permissions were restored in a finally block. A separate focused service test also confirmed that access(root, R_OK) succeeds before the same failure. No filesystem functions or analysis providers were simulated.

Expected behavior

Keep the selected directory refused while its children cannot be resolved, and preserve the existing access-denial classification, selected path and EACCES information. Initial read denial already becomes AnalysisAccessDeniedError; absence of search permission on a selected directory is the same host-access contract.

The selection-owner comment and classifier explicitly distinguish unreadable selected inputs from damaged artifacts. Existing selection tests cover mode 000 but not mode 0400.

Impact and scope

P2: structured callers receive generic artifact execution failure instead of host access denial and cannot read the errno from a structured field. Manual reading of details.detail identifies the denial. Where authorized and appropriate, restoring root search permission is a demonstrated workaround: the exact input then succeeds. No data corruption or successful analysis of inaccessible bytes was observed.

This case concerns permission on the caller-selected root, not a request to change partial-analysis behavior for isolated unreadable children. It was reproduced on one non-root POSIX host; Windows and natural permission races were not tested.

REA version

rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e.

Runtime and platform

Node.js v24.18.0 (repository pin), macOS 27.0.1 arm64. Built CLI replay and separate Vitest 5.0.3 service observation; real mode-bit permissions.

Target and artifact context

One selected application directory containing a readable JavaScript file; only the selected directory search/execute bit is removed.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

Failing CLI stdout, with local paths normalized:

{
  "error": "JavaScript application analysis failed",
  "code": "artifact_operation_failed",
  "category": "execution_failure",
  "message": "Artifact could not be read or written. Check file access and try again.",
  "retryable": false,
  "remediation": {
    "action": "Artifact could not be read or written. Check file access and try again."
  },
  "details": {
    "operation": "analyze_javascript_application",
    "reason": "io",
    "detail": "Could not inspect entry at <selected-directory>/index.js (EACCES): EACCES: permission denied, lstat '<selected-directory>/index.js'"
  }
}

Readable and permission-restored controls both exit 0 with application Evidence.

Typed outcome or recovery expected

Preserve the existing access_denied / unavailable semantics, selected path, system_code: "EACCES", filesystem-read boundary and applicable permission recovery for the selected root.

Analysis or suspected cause

The selection precheck checks only R_OK. The child metadata read fails and directoryIoFailure wraps the host error as ordinary ArtifactReaderFailure("io"). The service handles that wrapper before raw errno errors. This is source-derived attribution of the observed CLI failure. A root-open-only repair would not cover this host: DirectoryArtifactRootFailure is not the wrapper produced here.

Additional context

Related #5991 is a different producer: raw lstat, realpath and access exceptions reach the service's FILESYSTEM_ERROR_CODES catch and lose all diagnostic detail. This reproduction is already wrapped by the directory reader, retains errno/path in prose and enters the earlier artifactFailureToOperationError path. Fixing only the raw catch leaves this result unchanged.

#5229/PR #5230 address continuing past unreadable child entries in an otherwise searchable root and explicitly retain root failure. #5623/PR #5651 address ELOOP/ENAMETOOLONG resolution. #6064 is a keyed-archive read producer. They require different changes.

Upstream main aa51e4343963fdd0b97ec2ebdc3c41579fd9cb38 was fetched; the relevant permission check, directory-reader wrapper and translation are unchanged. That newer revision was not executed. No fix or regression range is established.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6116 Originally assigned to: @kaoru0822-kitauji on GitHub. ### 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 built `analyze-javascript-application` command reports a selected directory with read permission but no search permission as `artifact_operation_failed` / `execution_failure`, reason `io`. The result has no structured path, `system_code` or filesystem-read boundary. The actual `EACCES` and child path survive in `details.detail` as prose. On this host, the directory can be opened/listed; `lstat` on its `index.js` entry fails because the selected root lacks search permission. The same directory succeeds with mode 0700 before and after the failing mode-0400 call. ### Reproduction As a non-root POSIX user, create a caller-selected directory `$app` containing `index.js` with `export const x=1;`. From a prepared built checkout with pinned Node.js, run: ```sh chmod 0700 "$app" node scripts/rea.mjs analyze-javascript-application "$app" --json chmod 0400 "$app" node scripts/rea.mjs analyze-javascript-application "$app" --json chmod 0700 "$app" node scripts/rea.mjs analyze-javascript-application "$app" --json ``` This sequence was executed with isolated configuration/state. Exit statuses were 0, 1 and 0. Permissions were restored in a `finally` block. A separate focused service test also confirmed that `access(root, R_OK)` succeeds before the same failure. No filesystem functions or analysis providers were simulated. ### Expected behavior Keep the selected directory refused while its children cannot be resolved, and preserve the existing access-denial classification, selected path and `EACCES` information. Initial read denial already becomes `AnalysisAccessDeniedError`; absence of search permission on a selected directory is the same host-access contract. The [selection-owner comment and classifier](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/javascript/JavaScriptArtifactReconstruction.ts#L344-L379) explicitly distinguish unreadable selected inputs from damaged artifacts. Existing [selection tests](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/tests/boundary/filesystem/javascriptApplicationInputSelection.test.ts#L157-L169) cover mode 000 but not mode 0400. ### Impact and scope P2: structured callers receive generic artifact execution failure instead of host access denial and cannot read the errno from a structured field. Manual reading of `details.detail` identifies the denial. Where authorized and appropriate, restoring root search permission is a demonstrated workaround: the exact input then succeeds. No data corruption or successful analysis of inaccessible bytes was observed. This case concerns permission on the caller-selected root, not a request to change partial-analysis behavior for isolated unreadable children. It was reproduced on one non-root POSIX host; Windows and natural permission races were not tested. ### REA version rea-agents 6.7.0; source revision `52415975942a4b9c53a817915b313ccc404f280e`. ### Runtime and platform Node.js v24.18.0 (repository pin), macOS 27.0.1 arm64. Built CLI replay and separate Vitest 5.0.3 service observation; real mode-bit permissions. ### Target and artifact context One selected application directory containing a readable JavaScript file; only the selected directory search/execute bit is removed. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence Failing CLI stdout, with local paths normalized: ```json { "error": "JavaScript application analysis failed", "code": "artifact_operation_failed", "category": "execution_failure", "message": "Artifact could not be read or written. Check file access and try again.", "retryable": false, "remediation": { "action": "Artifact could not be read or written. Check file access and try again." }, "details": { "operation": "analyze_javascript_application", "reason": "io", "detail": "Could not inspect entry at <selected-directory>/index.js (EACCES): EACCES: permission denied, lstat '<selected-directory>/index.js'" } } ``` Readable and permission-restored controls both exit 0 with application Evidence. ### Typed outcome or recovery expected Preserve the existing `access_denied` / `unavailable` semantics, selected path, `system_code: "EACCES"`, filesystem-read boundary and applicable permission recovery for the selected root. ### Analysis or suspected cause The [selection precheck](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/javascript/JavaScriptArtifactReconstruction.ts#L350-L360) checks only `R_OK`. The [child metadata read](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/artifacts/DirectoryArtifactReader.ts#L189-L200) fails and [directoryIoFailure](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/artifacts/DirectoryArtifactReader.ts#L501-L520) wraps the host error as ordinary `ArtifactReaderFailure("io")`. The [service handles that wrapper before raw errno errors](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/javascript/JavaScriptApplicationService.ts#L142-L193). This is source-derived attribution of the observed CLI failure. A root-open-only repair would not cover this host: `DirectoryArtifactRootFailure` is not the wrapper produced here. ### Additional context Related [#5991](https://github.com/morluto/rea/issues/5991) is a different producer: raw `lstat`, `realpath` and `access` exceptions reach the service's `FILESYSTEM_ERROR_CODES` catch and lose all diagnostic detail. This reproduction is already wrapped by the directory reader, retains errno/path in prose and enters the earlier `artifactFailureToOperationError` path. Fixing only the raw catch leaves this result unchanged. #5229/PR #5230 address continuing past unreadable child entries in an otherwise searchable root and explicitly retain root failure. #5623/PR #5651 address ELOOP/ENAMETOOLONG resolution. #6064 is a keyed-archive read producer. They require different changes. Upstream main `aa51e4343963fdd0b97ec2ebdc3c41579fd9cb38` was fetched; the relevant permission check, directory-reader wrapper and translation are unchanged. That newer revision was not executed. No fix or regression range is established.
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

确认,和 #6117 是同一根线:目录"可读但不可搜索"是典型权限问题(lstat child 时 EACCES),却被压成泛化 artifact_operation_failed / io,结构化 path、system_code、filesystem-read boundary 全丢,EACCES 只躺在 details.detail 的 prose 里——typed caller 完全没法程序化处理。建议目录遍历遇到 child 访问错误时直接映射到已有 filesystem-read/access-denied 分类,把 child path 和 errno 升级为结构化字段;prose 细节保留可以,但不能是唯一载体。

<!-- gh-comment-id:6330502333 --> @XinwangTao commented on GitHub (Oct 22, 2026): 确认,和 #6117 是同一根线:目录"可读但不可搜索"是典型权限问题(lstat child 时 EACCES),却被压成泛化 artifact_operation_failed / io,结构化 path、system_code、filesystem-read boundary 全丢,EACCES 只躺在 details.detail 的 prose 里——typed caller 完全没法程序化处理。建议目录遍历遇到 child 访问错误时直接映射到已有 filesystem-read/access-denied 分类,把 child path 和 errno 升级为结构化字段;prose 细节保留可以,但不能是唯一载体。
Author
Owner

@kaoru0822-kitauji commented on GitHub (Oct 22, 2026):

Reproduced on current main d77bc0e8a (macOS arm64, non-root uid, Node from .nvmrc, built CLI, isolated config/state; real mode bits, permissions restored in a trap):

selected dir mode exit result
0700 0 Evidence
0400 1 artifact_operation_failed / execution_failure, reason: io, detail: "Could not inspect entry at <app>/index.js (EACCES): EACCES: permission denied, lstat '<app>/index.js'" (no path/system_code)
0000 1 access_denied / unavailable, path, system_code: EACCES, boundary: filesystem-read
0700 0 Evidence

I'm taking this together with #5991 (same selection boundary in JavaScriptArtifactReconstruction / JavaScriptApplicationService).

<!-- gh-comment-id:6331560942 --> @kaoru0822-kitauji commented on GitHub (Oct 22, 2026): Reproduced on current main `d77bc0e8a` (macOS arm64, non-root uid, Node from .nvmrc, built CLI, isolated config/state; real mode bits, permissions restored in a trap): | selected dir mode | exit | result | |---|---|---| | 0700 | 0 | Evidence | | 0400 | 1 | `artifact_operation_failed` / `execution_failure`, `reason: io`, `detail: "Could not inspect entry at <app>/index.js (EACCES): EACCES: permission denied, lstat '<app>/index.js'"` (no `path`/`system_code`) | | 0000 | 1 | `access_denied` / `unavailable`, `path`, `system_code: EACCES`, `boundary: filesystem-read` | | 0700 | 0 | Evidence | I'm taking this together with #5991 (same selection boundary in `JavaScriptArtifactReconstruction` / `JavaScriptApplicationService`).
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

kaoru0822-kitauji 的复现表把边界划得很清楚:mode 000 已经正确落到 access_denied,说明分类器本身没坏,坏的是 directoryIoFailure 这个 wrapper 抢在 raw errno 处理之前把 EACCES 降级成了 io。修复不用动 classification 语义,只需让 directory reader 的 wrapper 透传底层 errno(或 service 对来自 directory reader 的 wrapper 做同样的 errno 提升),mode 0400 就能与 mode 000 落到同一分类,也顺带覆盖 #5991 描述的 raw lstat/realpath 路径。

<!-- gh-comment-id:6332434362 --> @XinwangTao commented on GitHub (Oct 22, 2026): kaoru0822-kitauji 的复现表把边界划得很清楚:mode 000 已经正确落到 access_denied,说明分类器本身没坏,坏的是 directoryIoFailure 这个 wrapper 抢在 raw errno 处理之前把 EACCES 降级成了 io。修复不用动 classification 语义,只需让 directory reader 的 wrapper 透传底层 errno(或 service 对来自 directory reader 的 wrapper 做同样的 errno 提升),mode 0400 就能与 mode 000 落到同一分类,也顺带覆盖 #5991 描述的 raw lstat/realpath 路径。
Author
Owner

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

Checked the reported fix on main at d87e73f2db.

Selected-directory admission now checks R_OK|X_OK before traversal, so a readable but unsearchable selected root is classified through the existing host access-denial boundary. The regression runs mode 000 and 0400 between readable controls and asserts access_denied/unavailable with selected path, EACCES and filesystem-read. It is skipped on Windows/root and requires a real non-root POSIX filesystem to verify.

Implementation: JavaScriptArtifactReconstruction.ts, javascriptApplicationInputSelection.test.ts.

The relevant regression checks in javascriptApplicationInputSelection.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:6380740610 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. Selected-directory admission now checks R_OK|X_OK before traversal, so a readable but unsearchable selected root is classified through the existing host access-denial boundary. The regression runs mode 000 and 0400 between readable controls and asserts access_denied/unavailable with selected path, EACCES and filesystem-read. It is skipped on Windows/root and requires a real non-root POSIX filesystem to verify. Implementation: [JavaScriptArtifactReconstruction.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/javascript/JavaScriptArtifactReconstruction.ts#L445), [javascriptApplicationInputSelection.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/filesystem/javascriptApplicationInputSelection.test.ts#L161). The relevant regression checks in [javascriptApplicationInputSelection.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/filesystem/javascriptApplicationInputSelection.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#3750
No description provided.