Skip to content

security: use openFileWithinRoot for A2UI file serving#10525

Merged
steipete merged 3 commits intoopenclaw:mainfrom
opena2a-org:fix/a2ui-path-traversal
Feb 13, 2026
Merged

security: use openFileWithinRoot for A2UI file serving#10525
steipete merged 3 commits intoopenclaw:mainfrom
opena2a-org:fix/a2ui-path-traversal

Conversation

@thebenignhacker
Copy link
Copy Markdown
Contributor

@thebenignhacker thebenignhacker commented Feb 6, 2026

Summary

  • Replace hand-rolled path traversal defense in A2UI file-serving handler with the project's existing openFileWithinRoot primitive from fs-safe.ts
  • Closes a TOCTOU vulnerability where lstat + realpath checks were performed separately from the subsequent fs.readFile
  • Mirrors the established pattern already used in server.ts for canvas host file serving

Test plan

  • Build passes
  • Existing tests pass
  • Manual: verify A2UI assets load correctly in the control UI
  • Manual: verify path traversal attempts (e.g. ../../../etc/passwd) are rejected

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 6, 2026

Additional Comments (1)

src/canvas-host/a2ui.ts
HEAD requests return body

handleA2uiHttpRequest explicitly allows HEAD, but it always reads the file and calls res.end(...) with a body (both for HTML and non-HTML). For HEAD, Node will typically drop the body bytes, but you still do the file I/O and may send an unexpected response. Consider short-circuiting on req.method === "HEAD" after setting headers (and still closing result.handle) so HEAD is actually header-only.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/canvas-host/a2ui.ts
Line: 179:223

Comment:
**HEAD requests return body**

`handleA2uiHttpRequest` explicitly allows `HEAD`, but it always reads the file and calls `res.end(...)` with a body (both for HTML and non-HTML). For `HEAD`, Node will typically drop the body bytes, but you still do the file I/O and may send an unexpected response. Consider short-circuiting on `req.method === "HEAD"` after setting headers (and still closing `result.handle`) so `HEAD` is actually header-only.

How can I resolve this? If you propose a fix, please make it concise.

@thebenignhacker
Copy link
Copy Markdown
Contributor Author

Addressed the HEAD request feedback — pushed a fix that returns headers only (Content-Type, Cache-Control) without reading the file body for HEAD requests.

Ready for review.

@steipete steipete self-assigned this Feb 13, 2026
@steipete steipete force-pushed the fix/a2ui-path-traversal branch from d94eddc to ab7e254 Compare February 13, 2026 14:32
thebenignhacker and others added 3 commits February 13, 2026 15:32
Replace the custom path resolution in the A2UI handler with the existing
openFileWithinRoot primitive from fs-safe.ts. The previous implementation
had a TOCTOU (time-of-check-time-of-use) gap between lstat/realpath
checks and the actual file read, and did not use O_NOFOLLOW or inode
verification.

The canvas-host server.ts already uses openFileWithinRoot correctly for
its file serving. This change brings the A2UI handler in line with the
same security guarantees: atomic root-boundary enforcement, symlink
blocking via O_NOFOLLOW, and inode verification.
Return only headers (Content-Type, Cache-Control) without reading the
file body for HEAD requests, avoiding unnecessary disk I/O.
@steipete steipete force-pushed the fix/a2ui-path-traversal branch from ab7e254 to 64547d6 Compare February 13, 2026 14:36
@steipete steipete merged commit 7467fcc into openclaw:main Feb 13, 2026
9 checks passed
@steipete
Copy link
Copy Markdown
Contributor

Merged via squash.

Thanks @abdelsfane!

skyhawk14 pushed a commit to skyhawk14/openclaw that referenced this pull request Feb 13, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 64547d6
Co-authored-by: abdelsfane <32418586+abdelsfane@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
GwonHyeok pushed a commit to learners-superpumped/openclaw that referenced this pull request Feb 15, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 64547d6
Co-authored-by: abdelsfane <32418586+abdelsfane@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
jiulingyun added a commit to jiulingyun/openclaw-cn that referenced this pull request Feb 15, 2026
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 64547d6
Co-authored-by: abdelsfane <32418586+abdelsfane@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete

(cherry picked from commit 7467fcc)

# Conflicts:
#	CHANGELOG.md
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 64547d6
Co-authored-by: abdelsfane <32418586+abdelsfane@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete

(cherry picked from commit 7467fcc)

# Conflicts:
#	CHANGELOG.md
#	src/canvas-host/a2ui.ts
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 64547d6
Co-authored-by: abdelsfane <32418586+abdelsfane@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants