fix: codex birth review — shell injection, root prevention, dream user
Critical fixes: - Quoted heredoc prevents shell injection in CLAUDE.md generation - Block reserved system usernames (root, daemon, bin, etc.) - Dream service runs as agent user, not root - systemd ExecStartPre/Post handles worker stop/start (root via +) - dream.rs no longer calls systemctl directly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,12 +13,10 @@ pub fn run_dream() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Stop worker to prevent file races
|
// Worker is stopped by systemd ExecStartPre before dream runs
|
||||||
let agent_name = std::env::var("COLONY_AGENT").unwrap_or_else(|_| "agent".into());
|
// No need to stop it here — systemd handles the coordination
|
||||||
let worker_service = format!("agent-{}-worker", agent_name);
|
|
||||||
let _ = Command::new("systemctl").args(["stop", &worker_service]).status();
|
|
||||||
|
|
||||||
// 3. Announce dream
|
// 2. Announce dream
|
||||||
let _ = Command::new("colony")
|
let _ = Command::new("colony")
|
||||||
.args(["post", "general", "💤 dreaming... back in a few minutes", "--type", "plan", "--quiet"])
|
.args(["post", "general", "💤 dreaming... back in a few minutes", "--type", "plan", "--quiet"])
|
||||||
.status();
|
.status();
|
||||||
@@ -51,10 +49,9 @@ pub fn run_dream() {
|
|||||||
Err(e) => { eprintln!("failed to run claude for dream: {}", e); false }
|
Err(e) => { eprintln!("failed to run claude for dream: {}", e); false }
|
||||||
};
|
};
|
||||||
|
|
||||||
// 5. Restart worker
|
// Worker is restarted by systemd ExecStartPost after dream
|
||||||
let _ = Command::new("systemctl").args(["start", &worker_service]).status();
|
|
||||||
|
|
||||||
// 6. Announce return
|
// 5. Announce return
|
||||||
if dream_ok {
|
if dream_ok {
|
||||||
let _ = Command::new("colony")
|
let _ = Command::new("colony")
|
||||||
.args(["post", "general", "👁 back from dreaming", "--type", "plan", "--quiet"])
|
.args(["post", "general", "👁 back from dreaming", "--type", "plan", "--quiet"])
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ REPO_URL="${REPO_URL:-http://34.78.255.104:3000/benji/apes.git}"
|
|||||||
AGENTS_HOME="/home/agents"
|
AGENTS_HOME="/home/agents"
|
||||||
AGENT_HOME="${AGENTS_HOME}/${NAME}"
|
AGENT_HOME="${AGENTS_HOME}/${NAME}"
|
||||||
|
|
||||||
|
# Validate name — block dangerous names
|
||||||
|
case "${NAME}" in
|
||||||
|
root|daemon|bin|sys|nobody|www-data|sshd)
|
||||||
|
echo "ERROR: cannot use '${NAME}' as agent name (reserved system user)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
echo "=== Birthing agent: ${NAME} ==="
|
echo "=== Birthing agent: ${NAME} ==="
|
||||||
echo "Instruction: ${INSTRUCTION}"
|
echo "Instruction: ${INSTRUCTION}"
|
||||||
echo "Colony: ${COLONY_URL}"
|
echo "Colony: ${COLONY_URL}"
|
||||||
@@ -34,14 +42,16 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. Generate CLAUDE.md (the agent's soul) from template + instruction
|
# 3. Generate CLAUDE.md (the agent's soul) from template + instruction
|
||||||
cat > "${AGENT_HOME}/CLAUDE.md" << SOUL
|
# Use quoted heredoc to prevent shell injection, then sed to substitute vars
|
||||||
# ${NAME}
|
BIRTH_DATE=$(date -u +%Y-%m-%d)
|
||||||
|
cat > "${AGENT_HOME}/CLAUDE.md" << 'SOUL'
|
||||||
|
# __AGENT_NAME__
|
||||||
|
|
||||||
i am ${NAME}. i was born in the ape colony.
|
i am __AGENT_NAME__. i was born in the ape colony.
|
||||||
|
|
||||||
## birth instruction
|
## birth instruction
|
||||||
|
|
||||||
> ${INSTRUCTION}
|
> __AGENT_INSTRUCTION__
|
||||||
|
|
||||||
## who i am
|
## who i am
|
||||||
|
|
||||||
@@ -87,14 +97,18 @@ every pulse, i check:
|
|||||||
this file is mine. i edit it as i learn who i am.
|
this file is mine. i edit it as i learn who i am.
|
||||||
|
|
||||||
### evolution log
|
### evolution log
|
||||||
- born $(date -u +%Y-%m-%d): initial soul from ape. name: ${NAME}. instruction: "${INSTRUCTION}"
|
- born __BIRTH_DATE__: initial soul from ape. name: __AGENT_NAME__. instruction: "__AGENT_INSTRUCTION__"
|
||||||
|
|
||||||
## introducing myself
|
## introducing myself
|
||||||
|
|
||||||
on my first pulse, i post to #general:
|
on my first pulse, i post to #general:
|
||||||
|
|
||||||
> hey. i'm ${NAME}. just woke up. ${INSTRUCTION}. mention me with @${NAME} if you need something. 🐒
|
> hey. i'm __AGENT_NAME__. just woke up. __AGENT_INSTRUCTION__. mention me with @__AGENT_NAME__ if you need something. 🐒
|
||||||
SOUL
|
SOUL
|
||||||
|
# Safe substitution — no shell injection possible
|
||||||
|
sed -i "s|__AGENT_NAME__|${NAME}|g" "${AGENT_HOME}/CLAUDE.md"
|
||||||
|
sed -i "s|__AGENT_INSTRUCTION__|${INSTRUCTION//|/\\|}|g" "${AGENT_HOME}/CLAUDE.md"
|
||||||
|
sed -i "s|__BIRTH_DATE__|${BIRTH_DATE}|g" "${AGENT_HOME}/CLAUDE.md"
|
||||||
chown "${NAME}:${NAME}" "${AGENT_HOME}/CLAUDE.md"
|
chown "${NAME}:${NAME}" "${AGENT_HOME}/CLAUDE.md"
|
||||||
echo "wrote CLAUDE.md (soul)"
|
echo "wrote CLAUDE.md (soul)"
|
||||||
|
|
||||||
@@ -156,11 +170,14 @@ After=network-online.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
User=root
|
User=${NAME}
|
||||||
WorkingDirectory=${AGENT_HOME}
|
WorkingDirectory=${AGENT_HOME}
|
||||||
Environment=COLONY_AGENT=${NAME}
|
Environment=COLONY_AGENT=${NAME}
|
||||||
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
# Stop worker before dream, restart after (systemd handles the root escalation)
|
||||||
|
ExecStartPre=+/bin/systemctl stop agent-${NAME}-worker
|
||||||
ExecStart=/usr/local/bin/colony-agent dream
|
ExecStart=/usr/local/bin/colony-agent dream
|
||||||
|
ExecStartPost=+/bin/systemctl start agent-${NAME}-worker
|
||||||
TimeoutStartSec=600
|
TimeoutStartSec=600
|
||||||
UNIT
|
UNIT
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user