fix(convert): refuse symlinked tool output directories

This commit is contained in:
Rudy Celekli
2026-09-29 12:59:47 -04:00
parent 765dbfb652
commit 3acce92df2
3 changed files with 37 additions and 2 deletions
+3
View File
@@ -28,6 +28,9 @@ jobs:
- name: Reject colliding agent slugs before conversion
run: bash scripts/test-convert-slug-collisions.sh
- name: Refuse symlinked conversion output directories
run: bash scripts/test-convert-symlink-output.sh
- name: Validate converted outputs (round-trip, strict parse, counts, drift)
# Drift is advisory on pull requests (agent PRs always move their own manifest line;
# maintainers regenerate at landing) and enforced on pushes to main.
+5 -2
View File
@@ -692,6 +692,9 @@ clean_tool_output() {
# caller can never steer this rm -rf outside $OUT_DIR via "../" or "/".
[[ "$1" =~ ^[a-z0-9-]+$ ]] || { echo "ERROR: clean_tool_output: refusing non-slug tool name '$1'" >&2; return 1; }
local dir="$OUT_DIR/$1"
# The converter writes into this directory after cleaning it. Following a
# symlink here could overwrite an unrelated directory's existing agent files.
[[ ! -L "$dir" ]] || { error "refusing symlinked output directory: $dir"; return 1; }
[[ -d "$dir" ]] || return 0
find "$dir" -mindepth 1 -maxdepth 1 ! -name 'README.md' -exec rm -rf {} +
}
@@ -730,12 +733,12 @@ run_conversions() {
local count=0
if [[ "$tool" == "hermes" ]]; then
clean_tool_output "$tool"
clean_tool_output "$tool" || return 1
python3 "$SCRIPT_DIR/build-hermes-plugin.py" --repo-root "$REPO_ROOT" --out "$OUT_DIR/hermes"
return
fi
clean_tool_output "$tool"
clean_tool_output "$tool" || return 1
for dir in "${AGENT_DIRS[@]}"; do
local dirpath="$REPO_ROOT/$dir"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Conversion must not follow a tool-output symlink into unrelated files.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
scratch="$(mktemp -d)"
trap 'rm -rf "$scratch"' EXIT
mkdir -p "$scratch/repo/scripts" "$scratch/repo/engineering" \
"$scratch/output" "$scratch/private/agents"
cp "$SCRIPT_DIR/convert.sh" "$SCRIPT_DIR/lib.sh" "$scratch/repo/scripts/"
cat > "$scratch/repo/engineering/example.md" <<'EOF'
---
name: Example Agent
description: Example
color: blue
---
# Example Agent
EOF
printf 'private content\n' > "$scratch/private/agents/example-agent.md"
ln -s "$scratch/private" "$scratch/output/gemini-cli"
if bash "$scratch/repo/scripts/convert.sh" --tool gemini-cli --out "$scratch/output" > "$scratch/log" 2>&1; then
echo 'FAIL: converter accepted a symlinked output directory' >&2
exit 1
fi
grep -q 'refusing symlinked output' "$scratch/log"
[[ "$(cat "$scratch/private/agents/example-agent.md")" == 'private content' ]]
echo 'PASS: symlinked output refused before unrelated content changed'