Skip to content

fix(clawhub): sanitize archive temp filenames#56779

Merged
Takhoffman merged 1 commit intoopenclaw:mainfrom
soimy:fix/clawhub-scoped-archive-path-56452
Mar 29, 2026
Merged

fix(clawhub): sanitize archive temp filenames#56779
Takhoffman merged 1 commit intoopenclaw:mainfrom
soimy:fix/clawhub-scoped-archive-path-56452

Conversation

@soimy
Copy link
Copy Markdown
Contributor

@soimy soimy commented Mar 29, 2026

Summary

  • Problem: downloadClawHubPackageArchive() built the temp zip path from the raw package name, so scoped names like @soimy/dingtalk turned into <tmp>/@soimy/dingtalk.zip and failed with ENOENT before extraction.
  • Why it matters: the documented ClawHub-first openclaw plugins install <package> flow was blocked for scoped plugin packages.
  • What changed: reuse safeDirName() for ClawHub package and skill archive temp filenames, add focused unit coverage for slash-containing package names and slugs, and note the user-visible fix in CHANGELOG.md.
  • What did NOT change (scope boundary): no changes to ClawHub resolution, extraction, verification, install-dir layout, or plugin manifest handling.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • 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

Root Cause / Regression History (if applicable)

  • Root cause: src/infra/clawhub.ts used path.join(tmpDir, ${params.name}.zip) and path.join(tmpDir, ${params.slug}.zip), so slash-containing identifiers were treated as nested directories instead of temp file basenames.
  • Missing detection / guardrail: there was no unit test that exercised archive downloads for slash-containing package names or skill slugs.
  • Prior context (git blame, prior PR, issue, or refactor if known): OpenClaw already used safeDirName() in src/plugins/install.ts for install-directory naming, but the ClawHub temp archive path missed that normalization.
  • Why this regressed now: ClawHub package resolution supports scoped package names, but the local archive write path still assumed identifiers never contain separators.
  • If unknown, what was ruled out: ruled out ClawHub lookup/auth failures because the failure happens after download bytes are available and exactly at the local fs.writeFile() call.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/infra/clawhub.test.ts
  • Scenario the test should lock in: slash-containing package names and skill slugs are written to sanitized temp zip filenames instead of nested paths.
  • Why this is the smallest reliable guardrail: the bug is entirely in the local archive-path construction logic, so a focused unit test catches it without needing live ClawHub/network setup.
  • Existing test that already covers this (if any): None.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

openclaw plugins install @scope/name no longer fails during the ClawHub archive download step just because the package name contains /.

Diagram (if applicable)

Before:
openclaw plugins install @soimy/dingtalk
-> download bytes from ClawHub
-> write <tmp>/@soimy/dingtalk.zip
-> ENOENT

After:
openclaw plugins install @soimy/dingtalk
-> download bytes from ClawHub
-> write <tmp>/@soimy__dingtalk.zip
-> continue to extraction/install

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS (local verification), issue also reports Ubuntu 22.04
  • Runtime/container: Node 22 / pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): ClawHub plugin install flow
  • Relevant config (redacted): N/A

Steps

  1. Start from a build before this fix.
  2. Run openclaw plugins install @soimy/dingtalk.
  3. Observe the local archive write fail before extraction.

Expected

  • The downloaded archive is written to a valid temp zip file path and install continues.

Actual

  • The archive path includes the raw scoped package slash and fails with ENOENT during fs.writeFile().

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: added regression tests for @soimy/dingtalk and ops/calendar; confirmed they fail before the fix with ENOENT and pass after the fix.
  • Edge cases checked: package names and skill slugs containing / now map to safe temp zip basenames using the repo's existing safeDirName() convention.
  • What you did not verify: a live end-to-end install against ClawHub with a real published package.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: temp archive basenames now differ from the raw ClawHub identifier.
    • Mitigation: these files are internal ephemeral temp artifacts only, and the sanitization matches the existing install-path naming helper already used elsewhere in plugin install code.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 29, 2026

Greptile Summary

This PR fixes a targeted ENOENT bug in src/infra/clawhub.ts where temp archive paths were constructed directly from raw package names or skill slugs. Identifiers containing / (e.g. scoped npm names like @soimy/dingtalk, or skill slugs like ops/calendar) caused path.join to treat the prefix as a subdirectory that doesn't exist, crashing the write before extraction could begin.

Changes:

  • downloadClawHubPackageArchive: wraps params.name with safeDirName() before building the .zip path, replacing / and \ with __.
  • downloadClawHubSkillArchive: same fix for params.slug.
  • Two new regression tests added that exercise slash-containing identifiers, assert the sanitized basename, and verify file contents — with proper finally-block temp cleanup.
  • CHANGELOG.md updated with a user-facing entry referencing the linked issue.

The fix reuses the existing safeDirName() helper already used in src/plugins/install.ts for install-directory naming, keeping the encoding convention consistent across the codebase. No changes to ClawHub resolution, extraction, or plugin manifest handling.

Confidence Score: 5/5

Safe to merge — minimal, well-tested fix with no side effects outside the temp archive filename.

The change is two one-line substitutions that apply the already-established safeDirName() helper to the two archive-path construction sites. safeDirName() is already tested and used elsewhere in the codebase for the same purpose. The new regression tests cover both failure modes directly, and all other PR scope (resolution, extraction, install dir, manifest) is explicitly unchanged. No P0 or P1 issues found.

No files require special attention.

Important Files Changed

Filename Overview
src/infra/clawhub.ts Both archive-path constructions now wrap the raw identifier with safeDirName(), replacing / and \ with __ before the filename is passed to fs.writeFile. The fix is minimal, correctly scoped, and consistent with how the rest of the plugin-install code already handles identifier encoding.
src/infra/clawhub.test.ts Two new regression tests added: one for scoped package names (@soimy/dingtalk → @soimy__dingtalk.zip) and one for slash-containing skill slugs (ops/calendar → ops__calendar.zip). Both tests assert the sanitized basename and the file contents, and clean up the temp directory in a finally block.
CHANGELOG.md One-line entry added to the unreleased section, accurately describing the user-visible fix and linking the issue number.

Reviews (1): Last reviewed commit: "fix(clawhub): sanitize archive temp file..." | Re-trigger Greptile

@Takhoffman Takhoffman force-pushed the fix/clawhub-scoped-archive-path-56452 branch from 8974b68 to 37e3991 Compare March 29, 2026 05:25
@Takhoffman Takhoffman force-pushed the fix/clawhub-scoped-archive-path-56452 branch from 37e3991 to de12876 Compare March 29, 2026 05:37
@Takhoffman Takhoffman merged commit eee8e96 into openclaw:main Mar 29, 2026
9 checks passed
@Takhoffman
Copy link
Copy Markdown
Contributor

Merged in eee8e96 after verification with pnpm build, pnpm check, and pnpm test. I rebased the PR branch onto current main, carried the ClawHub temp-filename fix and its focused tests through two unreleased changelog conflicts, and pushed the refreshed head before merge. The changelog entry is in CHANGELOG.md under ### Fixes.

Alix-007 pushed a commit to Alix-007/openclaw that referenced this pull request Mar 30, 2026
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
alexjiang1 pushed a commit to alexjiang1/openclaw that referenced this pull request Mar 31, 2026
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Tardisyuan pushed a commit to Tardisyuan/openclaw that referenced this pull request Apr 30, 2026
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
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.

[Bug]: Scoped ClawHub plugin packages can fail to install with ENOENT because the temp archive path uses the raw package name

2 participants