Skip to content

fix(tools): expand tilde in toRelativePathInRoot path validation#30788

Closed
Sid-Qin wants to merge 2 commits intoopenclaw:mainfrom
Sid-Qin:fix/30773-write-tool-tilde-verification
Closed

fix(tools): expand tilde in toRelativePathInRoot path validation#30788
Sid-Qin wants to merge 2 commits intoopenclaw:mainfrom
Sid-Qin:fix/30773-write-tool-tilde-verification

Conversation

@Sid-Qin
Copy link
Contributor

@Sid-Qin Sid-Qin commented Mar 1, 2026

Summary

  • Problem: Write tool in isolated cron agentTurn jobs reports failure (lastRunStatus: "error") even when files are successfully created on disk. consecutiveErrors increments indefinitely.
  • Why it matters: Every isolated cron job that writes memory files (e.g. morning standups) shows persistent error state despite working correctly, making monitoring unreliable.
  • What changed: toRelativePathInRoot in src/agents/pi-tools.read.ts now applies expandHomePrefix() to both root and candidate before path.resolve(), matching the expansion already done in fs-safe.ts.
  • What did NOT change: writeFileWithinRoot in fs-safe.ts (already handles tilde), no other tool paths affected.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Cron jobs writing to ~/agents/.../memory/ paths will no longer show false errors
  • lastRunStatus will correctly report "ok" when files are written successfully
  • consecutiveErrors will not increment on successful writes

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Ubuntu (Linux 6.17.0-14-generic x64)
  • Runtime: Node.js
  • Integration/channel: Cron (isolated agentTurn)

Steps

  1. Create a cron job with sessionTarget: "isolated" and payload.kind: "agentTurn"
  2. Instruct agent to write to ~/agents/<project>/memory/DATE.md
  3. Observe cron job state

Expected

  • File is written AND lastRunStatus: "ok"

Actual

  • Before fix: File is written BUT lastRunStatus: "error", consecutiveErrors increments
  • After fix: File is written AND lastRunStatus: "ok"

Evidence

Root cause: path.resolve("~/agents/...") treats ~ as a literal directory name, producing $CWD/~/agents/... instead of /home/user/agents/.... The path verification then fails with "Path escapes workspace root" even though writeFileWithinRoot (which does expand ~) succeeded.

Fix adds expandHomePrefix() call before path.resolve():

const rootResolved = path.resolve(expandHomePrefix(root));
const resolved = path.resolve(expandHomePrefix(candidate));

Human Verification (required)

  • Verified scenarios: tilde paths resolve correctly after fix, non-tilde paths unaffected
  • Edge cases checked: absolute paths, relative paths without tilde
  • What I did not verify: Windows-specific path handling

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert: Revert the commit
  • Files/config to restore: src/agents/pi-tools.read.ts
  • Known bad symptoms: Write tool path validation would break for tilde paths

Risks and Mitigations

None — expandHomePrefix is already used extensively throughout the codebase for the same purpose.

SidQin-cyber added 2 commits March 2, 2026 00:13
Croner can return a past-year timestamp for some timezone/date
combinations (e.g. Asia/Shanghai).  When nextRun returns a value at or
before nowMs, retry from the next whole second and, if still stale,
from midnight-tomorrow UTC before giving up.

Closes openclaw#30351
The write tool's post-write path verification in toRelativePathInRoot
uses path.resolve() which does not expand ~ (tilde). When cron jobs
write to paths like ~/agents/.../memory/DATE.md, the verification
fails with "Path escapes workspace root" even though the file was
successfully written by writeFileWithinRoot (which does expand ~).

Apply expandHomePrefix() to both root and candidate before resolving,
matching the expansion already done in fs-safe.ts.

Closes openclaw#30773
@aisle-research-bot
Copy link

aisle-research-bot bot commented Mar 1, 2026

🔒 Aisle Security Analysis

✅ We scanned this PR and did not find any security vulnerabilities.

Aisle supplements but does not replace security review.


Analyzed PR: #30788 at commit 16b22f4

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 1, 2026

Greptile Summary

This PR includes two separate bug fixes:

1. Tilde expansion in path validation (primary fix):

  • Fixes Write tool reporting false errors in isolated cron jobs even when files are successfully created
  • Root cause: toRelativePathInRoot didn't expand ~ before path.resolve(), causing validation to fail while writeFileWithinRoot (which does expand ~) succeeded
  • Solution: Added expandHomePrefix() calls to both root and candidate parameters before path resolution
  • Impact: Cron jobs writing to ~/agents/.../memory/ paths will now correctly report success

2. Croner year-rollback workaround (included but undocumented):

  • Fixes issue where croner library returns past-year timestamps for certain timezone/date combinations (e.g., Asia/Shanghai)
  • Solution: Added multi-level retry logic when nextRun() returns a timestamp in the past
  • Includes new test case to prevent regression

Both fixes are technically sound with no logical errors or security concerns. The tilde expansion follows existing patterns in the codebase (expandHomePrefix is already used in fs-safe.ts), and the cron fix includes proper test coverage.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • Both fixes are simple, focused, and address real bugs without introducing new issues. The tilde expansion follows existing patterns in the codebase, and the cron workaround includes test coverage. No security vulnerabilities or logical errors detected.
  • No files require special attention

Last reviewed commit: 16b22f4

thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 1, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 1, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 1, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 1, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 1, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 1, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 3, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Mar 3, 2026
path.resolve() treats ~ as a literal directory name, so ~/file.txt
resolved to <cwd>/~/file.txt instead of /home/user/file.txt. Add
expandHomePrefix() before path.resolve() in all affected tool path
resolution points:

- Host write operations (mkdir, writeFile) in non-workspace mode
- Host edit operations (readFile, writeFile, access) in non-workspace mode
- toRelativePathInRoot() workspace boundary validator (affects all tools)
- resolveWorkdir() for non-sandbox exec/bash working directory
- parseSandboxBindMount() for Docker bind mount host paths

The host read tool and sandbox tools already handled tilde via the
upstream library's expandPath().

Closes openclaw#30669
Related: openclaw#30782, openclaw#30788, openclaw#30744, openclaw#30770, openclaw#30756, openclaw#30753, openclaw#30752, openclaw#30747
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Write tool reports error in isolated cron agentTurn jobs despite file being successfully created

2 participants