[GH-ISSUE #3324] [Bug] SQLite inspection reads the wrong database after a symlink parent component #1192

Closed
opened 2026-10-15 18:31:53 +00:00 by bsakaguchi · 2 comments
Owner

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

Before submitting

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

Area

CLI

Observed behavior

inspect-sqlite-database reads a different database when the selected path contains a directory symlink followed by ... On macOS, link/../sample.db selects the database beside the symlink's target directory. The CLI instead returns successful, observed Evidence for the database beside the symlink itself, including that different file's digest and rows.

Both commands exit 0. The first Evidence record identifies the sibling sample.db, labels its confidence observed, and reports that sibling's SHA-256. The record is internally consistent for the wrong file, so its success status does not reveal that the caller's selection changed. The same failure reproduced with an absolute path containing the symlink and parent component.

Reproduction

  1. Start from a clean checkout at the baseline below, install the locked dependencies, and build the runtime. These are preparation commands for a clean checkout; the observed reproduction used the built source runtime. Use a Node.js version with the SQLite capability required by the documented workflow.
git clone https://github.com/morluto/rea.git rea-repro
cd rea-repro
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached
  1. From the repository root, run the following commands. They create every input file inline, including two stable ordinary SQLite databases with one row each and an isolated configuration home.
REA_ENTRYPOINT="$PWD/scripts/rea.mjs"
mkdir rea-sqlite-path-repro
cd rea-sqlite-path-repro
mkdir home
python3 - <<'PYFIXTURE'
from pathlib import Path
import sqlite3
Path("target/child").mkdir(parents=True)
Path("link").symlink_to("target/child", target_is_directory=True)
for name, marker in [("sample.db", "WRONG"), ("target/sample.db", "SELECTED")]:
    connection = sqlite3.connect(name)
    connection.execute("CREATE TABLE marker (value TEXT)")
    connection.execute("INSERT INTO marker VALUES (?)", (marker,))
    connection.commit()
    connection.close()
assert Path("link/../sample.db").read_bytes() == Path("target/sample.db").read_bytes()
PYFIXTURE
HOME="$PWD/home" node "$REA_ENTRYPOINT" inspect-sqlite-database link/../sample.db --table marker --json > selected.json
printf 'selected exit: %s\n' "$?"
HOME="$PWD/home" node "$REA_ENTRYPOINT" inspect-sqlite-database target/sample.db --table marker --json > control.json
printf 'control exit: %s\n' "$?"
python3 - <<'PYRESULT'
import json
for name in ["selected", "control"]:
    evidence = json.load(open(name + ".json"))
    row = evidence["normalized_result"]["rows"]["values"][0][0]
    print(name, row["value"])
PYRESULT
  1. Observe selected WRONG and control SELECTED, with both commands exiting 0. The byte comparison proves that the selected filesystem path names target/sample.db, so the two CLI inputs should identify the same database.

Expected behavior

The CLI should inspect the database selected by the operating system's path semantics and return SELECTED for both commands. Resolving a CLI path against the operator's working directory must preserve the selected file, its original digest, and its row values.

docs/sqlite-databases.md:3 defines inspection of one explicitly selected local snapshot. docs/sqlite-databases.md:36 permits operator-relative CLI paths. docs/sqlite-databases.md:75 binds Evidence to the original database identity. AGENTS.md:13, under Product and Authority, requires preservation of caller-selected paths.

Impact and scope

Impact level: P2, supported. The CLI silently returns rows and artifact identity from a different local database for a valid selected path. Relative and absolute spellings containing the symlink followed by .. reproduced the failure. Source files were not modified by inspection.

Workaround status: available. Resolve the selected path through the filesystem first and pass the resulting path, or supply target/sample.db directly. Both the filesystem-resolved absolute path and the direct target path were verified to return SELECTED.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1; arm64; built CLI runtime. The reported SQLite engine was 3.53.4. Python's standard-library SQLite module created the two local fixtures. No native analysis engine or external service was used.

Target and artifact context

Two stable ordinary local SQLite databases contain the table marker with one text column named value and one row each. sample.db contains WRONG, while target/sample.db contains SELECTED. The directory symlink link points to target/child; the filesystem resolves link/../sample.db to target/sample.db. The command explicitly selects --table marker and returns JSON Evidence.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

The executed public CLI reproduction returned:

selected exit: 0
control exit: 0
selected WRONG
control SELECTED

The byte-identity assertion succeeds before inspection. The public CLI returned different rows for paths that the filesystem resolves to the same file. The first result also identifies the wrong sibling file and reports that file's original SHA-256 with confidence observed.

Typed outcome or recovery expected

Repeat the fixture commands and byte-identity assertion. Both CLI results must contain SELECTED and identify the same original database digest. Check an absolute spelling containing the same symlink and .. as well as the relative spelling. Keep ordinary relative paths and explicit table selection working. The sibling database must remain distinct and must still return WRONG when the caller explicitly selects it.

Analysis or suspected cause

Source-derived reasoning: src/cli/sqliteCommands.ts:58 resolves the supplied path before forwarding it to the shared inspection workflow. Lexical path conversion removes the symlink and parent-component traversal that determines which file the operating system selects. Source inspection locates this adapter conversion separately from the observed CLI calls and byte-identity assertion.

Additional context

Related #3303 loses literal symlink target distinctions during process capture; this report changes the selected database through SQLite CLI argument resolution.

Related #2822 misclassifies a Windows exclusive-sharing read failure; this report returns successful Evidence for a different database on macOS.

Related #2614, closed as completed, concerns missing database path failure reasons; both database files here exist and inspection succeeds.

Related #2613, closed as completed, concerns correction metadata for an unknown column; the valid table selection here returns the wrong database's rows.

Related #2612, closed as completed, loses completed observations after worker cleanup failure; this report returns successful Evidence without a cleanup failure.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3324 ### 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 CLI ### Observed behavior `inspect-sqlite-database` reads a different database when the selected path contains a directory symlink followed by `..`. On macOS, `link/../sample.db` selects the database beside the symlink's target directory. The CLI instead returns successful, observed Evidence for the database beside the symlink itself, including that different file's digest and rows. Both commands exit 0. The first Evidence record identifies the sibling `sample.db`, labels its confidence `observed`, and reports that sibling's SHA-256. The record is internally consistent for the wrong file, so its success status does not reveal that the caller's selection changed. The same failure reproduced with an absolute path containing the symlink and parent component. ### Reproduction 1. Start from a clean checkout at the baseline below, install the locked dependencies, and build the runtime. These are preparation commands for a clean checkout; the observed reproduction used the built source runtime. Use a Node.js version with the SQLite capability required by the documented workflow. ```sh git clone https://github.com/morluto/rea.git rea-repro cd rea-repro git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` 2. From the repository root, run the following commands. They create every input file inline, including two stable ordinary SQLite databases with one row each and an isolated configuration home. ```sh REA_ENTRYPOINT="$PWD/scripts/rea.mjs" mkdir rea-sqlite-path-repro cd rea-sqlite-path-repro mkdir home python3 - <<'PYFIXTURE' from pathlib import Path import sqlite3 Path("target/child").mkdir(parents=True) Path("link").symlink_to("target/child", target_is_directory=True) for name, marker in [("sample.db", "WRONG"), ("target/sample.db", "SELECTED")]: connection = sqlite3.connect(name) connection.execute("CREATE TABLE marker (value TEXT)") connection.execute("INSERT INTO marker VALUES (?)", (marker,)) connection.commit() connection.close() assert Path("link/../sample.db").read_bytes() == Path("target/sample.db").read_bytes() PYFIXTURE HOME="$PWD/home" node "$REA_ENTRYPOINT" inspect-sqlite-database link/../sample.db --table marker --json > selected.json printf 'selected exit: %s\n' "$?" HOME="$PWD/home" node "$REA_ENTRYPOINT" inspect-sqlite-database target/sample.db --table marker --json > control.json printf 'control exit: %s\n' "$?" python3 - <<'PYRESULT' import json for name in ["selected", "control"]: evidence = json.load(open(name + ".json")) row = evidence["normalized_result"]["rows"]["values"][0][0] print(name, row["value"]) PYRESULT ``` 3. Observe `selected WRONG` and `control SELECTED`, with both commands exiting 0. The byte comparison proves that the selected filesystem path names `target/sample.db`, so the two CLI inputs should identify the same database. ### Expected behavior The CLI should inspect the database selected by the operating system's path semantics and return `SELECTED` for both commands. Resolving a CLI path against the operator's working directory must preserve the selected file, its original digest, and its row values. `docs/sqlite-databases.md:3` defines inspection of one explicitly selected local snapshot. `docs/sqlite-databases.md:36` permits operator-relative CLI paths. `docs/sqlite-databases.md:75` binds Evidence to the original database identity. `AGENTS.md:13`, under Product and Authority, requires preservation of caller-selected paths. ### Impact and scope Impact level: P2, supported. The CLI silently returns rows and artifact identity from a different local database for a valid selected path. Relative and absolute spellings containing the symlink followed by `..` reproduced the failure. Source files were not modified by inspection. Workaround status: available. Resolve the selected path through the filesystem first and pass the resulting path, or supply `target/sample.db` directly. Both the filesystem-resolved absolute path and the direct target path were verified to return `SELECTED`. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1; arm64; built CLI runtime. The reported SQLite engine was 3.53.4. Python's standard-library SQLite module created the two local fixtures. No native analysis engine or external service was used. ### Target and artifact context Two stable ordinary local SQLite databases contain the table `marker` with one text column named `value` and one row each. `sample.db` contains `WRONG`, while `target/sample.db` contains `SELECTED`. The directory symlink `link` points to `target/child`; the filesystem resolves `link/../sample.db` to `target/sample.db`. The command explicitly selects `--table marker` and returns JSON Evidence. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence The executed public CLI reproduction returned: ```text selected exit: 0 control exit: 0 selected WRONG control SELECTED ``` The byte-identity assertion succeeds before inspection. The public CLI returned different rows for paths that the filesystem resolves to the same file. The first result also identifies the wrong sibling file and reports that file's original SHA-256 with confidence `observed`. ### Typed outcome or recovery expected Repeat the fixture commands and byte-identity assertion. Both CLI results must contain `SELECTED` and identify the same original database digest. Check an absolute spelling containing the same symlink and `..` as well as the relative spelling. Keep ordinary relative paths and explicit table selection working. The sibling database must remain distinct and must still return `WRONG` when the caller explicitly selects it. ### Analysis or suspected cause Source-derived reasoning: `src/cli/sqliteCommands.ts:58` resolves the supplied path before forwarding it to the shared inspection workflow. Lexical path conversion removes the symlink and parent-component traversal that determines which file the operating system selects. Source inspection locates this adapter conversion separately from the observed CLI calls and byte-identity assertion. ### Additional context Related [#3303](https://github.com/morluto/rea/issues/3303) loses literal symlink target distinctions during process capture; this report changes the selected database through SQLite CLI argument resolution. Related [#2822](https://github.com/morluto/rea/issues/2822) misclassifies a Windows exclusive-sharing read failure; this report returns successful Evidence for a different database on macOS. Related [#2614](https://github.com/morluto/rea/issues/2614), closed as completed, concerns missing database path failure reasons; both database files here exist and inspection succeeds. Related [#2613](https://github.com/morluto/rea/issues/2613), closed as completed, concerns correction metadata for an unknown column; the valid table selection here returns the wrong database's rows. Related [#2612](https://github.com/morluto/rea/issues/2612), closed as completed, loses completed observations after worker cleanup failure; this report returns successful Evidence without a cleanup failure.
Author
Owner

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

This no longer reproduces on current main (5120eb441). It appears fixed by #4067 (fc791bb60, fix(paths): select caller paths through symlinks and .. as the filesystem does, merged 2026-10-15, a day after this report). That change replaced the lexical path.resolve in src/cli/sqliteCommands.ts with the shared absoluteCallerPath owner (src/filesystem/callerPath.ts), which leaves .. for the OS to resolve on POSIX.

Executed on macOS arm64 (system Node 26.5.0): I added this report's fixture as a public CLI and MCP boundary case. Two node:sqlite databases are marked WRONG and SELECTED, with link -> target/child, in a test-owned temp dir. The case covers link/../sample.db and <root>/link/../sample.db via inspect-sqlite-database --table marker, and the absolute spelling via MCP inspect_sqlite_database.

  • On main, npm run test:focused -- tests/boundary/cli/callerPathSymlinkParentCli.test.ts passed 3/3. Evidence reports target/sample.db's SHA-256 and rows [{value: "SELECTED"}].
  • Control: temporarily reverting the adapter to resolve(args.path) makes the case fail (expected "SELECTED", received "WRONG"). Restoring main's source passes again.

I did not run the original Python script or Windows. Leaving closure to a maintainer.

<!-- gh-comment-id:6232970415 --> @kaoru0822-kitauji commented on GitHub (Oct 17, 2026): This no longer reproduces on current main (5120eb441). It appears fixed by #4067 (fc791bb60, `fix(paths): select caller paths through symlinks and .. as the filesystem does`, merged 2026-10-15, a day after this report). That change replaced the lexical `path.resolve` in `src/cli/sqliteCommands.ts` with the shared `absoluteCallerPath` owner (`src/filesystem/callerPath.ts`), which leaves `..` for the OS to resolve on POSIX. Executed on macOS arm64 (system Node 26.5.0): I added this report's fixture as a public CLI and MCP boundary case. Two `node:sqlite` databases are marked WRONG and SELECTED, with `link -> target/child`, in a test-owned temp dir. The case covers `link/../sample.db` and `<root>/link/../sample.db` via `inspect-sqlite-database --table marker`, and the absolute spelling via MCP `inspect_sqlite_database`. - On main, `npm run test:focused -- tests/boundary/cli/callerPathSymlinkParentCli.test.ts` passed 3/3. Evidence reports `target/sample.db`'s SHA-256 and rows `[{value: "SELECTED"}]`. - Control: temporarily reverting the adapter to `resolve(args.path)` makes the case fail (`expected "SELECTED", received "WRONG"`). Restoring main's source passes again. I did not run the original Python script or Windows. Leaving closure to a maintainer.
Author
Owner

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

Addressed on main by github.com/morluto/rea@fc791bb606. The SQLite CLI now uses absoluteCallerPath, preserving symlink/.. traversal for POSIX filesystem resolution for both relative and absolute input spellings. The selected path passes through to SQLite snapshot acquisition without lexical collapse. I checked the current source and shared path regression coverage; I have not rerun the SQLite fixture. Closing as completed.

<!-- gh-comment-id:6244133141 --> @coygeek commented on GitHub (Oct 18, 2026): Addressed on main by https://github.com/morluto/rea/commit/fc791bb606d56d0b5c986237886b9eb59c425825. The SQLite CLI now uses absoluteCallerPath, preserving symlink/.. traversal for POSIX filesystem resolution for both relative and absolute input spellings. The selected path passes through to SQLite snapshot acquisition without lexical collapse. I checked the current source and shared path regression coverage; I have not rerun the SQLite fixture. Closing as completed.
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#1192
No description provided.