Compare commits

..

1 commit

Author SHA1 Message Date
Agent
6758f10a7e fix: SECURITY: Replace eval usage with safer alternatives (#59)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
2026-03-31 18:10:14 +00:00
3 changed files with 19 additions and 84 deletions

View file

@ -9,8 +9,6 @@ You are helping the user set up and operate a **disinto autonomous code factory*
of bash scripts and Claude CLI that automates the full development lifecycle: picking up
issues, implementing via Claude, creating PRs, running CI, reviewing, merging, and mirroring.
This guide shows how to set up the factory to develop an **external project** (e.g., `johba/harb`).
## First-time setup
Walk the user through these steps interactively. Ask questions where marked with [ASK].
@ -29,34 +27,20 @@ docker --version && git --version && jq --version && curl --version && tmux -V &
Any missing tool — help the user install it before continuing.
### 2. Clone disinto and choose a target project
### 2. Clone and init
Clone the disinto factory itself:
```bash
git clone https://codeberg.org/johba/disinto.git && cd disinto
```
[ASK] What repository should the factory develop? Provide the **remote repository URL** in one of these formats:
- Full URL: `https://github.com/johba/harb.git` or `https://codeberg.org/johba/harb.git`
- Short slug: `johba/harb` (uses local Forgejo as the primary remote)
[ASK] What repo should the factory develop? Options:
- **Itself** (self-development): `bin/disinto init https://codeberg.org/johba/disinto --yes --repo-root $(pwd)`
- **Another project**: `bin/disinto init <repo-url> --yes`
The factory will clone from the remote URL (if provided) or from your local Forgejo, then mirror to the remote.
Then initialize the factory for that project:
```bash
bin/disinto init johba/harb --yes
# or with full URL:
bin/disinto init https://github.com/johba/harb.git --yes
```
The `init` command will:
- Create all bot users (dev-bot, review-bot, etc.) on the local Forgejo
- Generate and save `WOODPECKER_TOKEN`
- Start the stack containers
- Clone the target repo into the agent workspace
> **Note:** The `--repo-root` flag is optional and only needed if you want to customize
> where the cloned repo lives. By default, it goes under `/home/agent/repos/<name>`.
Run the init and watch for:
- All bot users created (dev-bot, review-bot, etc.)
- `WOODPECKER_TOKEN` generated and saved
- Stack containers all started
### 3. Post-init verification
@ -86,48 +70,7 @@ docker exec disinto-agents-1 chown -R agent:agent /home/agent/repos
docker exec -u agent disinto-agents-1 bash -c "source /home/agent/disinto/.env && git clone http://dev-bot:\${FORGE_TOKEN}@forgejo:3000/<org>/<repo>.git /home/agent/repos/<name>"
```
### 4. Create the project configuration file
The factory uses a TOML file to configure how it manages your project. Create
`projects/<name>.toml` based on the template format:
```toml
# projects/harb.toml
name = "harb"
repo = "johba/harb"
forge_url = "http://localhost:3000"
repo_root = "/home/agent/repos/harb"
primary_branch = "master"
[ci]
woodpecker_repo_id = 0
stale_minutes = 60
[services]
containers = ["ponder"]
[monitoring]
check_prs = true
check_dev_agent = true
check_pipeline_stall = true
# [mirrors]
# github = "git@github.com:johba/harb.git"
# codeberg = "git@codeberg.org:johba/harb.git"
```
**Key fields:**
- `name`: Project identifier (used for file names, logs, etc.)
- `repo`: The source repo in `owner/name` format
- `forge_url`: URL of your local Forgejo instance
- `repo_root`: Where the agent clones the repo
- `primary_branch`: Default branch name (e.g., `main` or `master`)
- `woodpecker_repo_id`: Set to `0` initially; auto-populated on first CI run
- `containers`: List of Docker containers the factory should manage
- `mirrors`: Optional external forge URLs for backup/sync
### 5. Mirrors (optional)
### 4. Mirrors (optional)
[ASK] Should the factory mirror to external forges? If yes, which?
- GitHub: need repo URL and SSH key added to GitHub account
@ -145,7 +88,7 @@ ssh -T git@github.com 2>&1; ssh -T git@codeberg.org 2>&1
If SSH host keys are missing: `ssh-keyscan github.com codeberg.org >> ~/.ssh/known_hosts 2>/dev/null`
Edit `projects/<name>.toml` to uncomment and configure mirrors:
Edit `projects/<name>.toml` to add mirrors:
```toml
[mirrors]
github = "git@github.com:Org/repo.git"
@ -157,7 +100,7 @@ Test with a manual push:
source .env && source lib/env.sh && export PROJECT_TOML=projects/<name>.toml && source lib/load-project.sh && source lib/mirrors.sh && mirror_push
```
### 6. Seed the backlog
### 5. Seed the backlog
[ASK] What should the factory work on first? Brainstorm with the user.
@ -185,12 +128,10 @@ Use labels:
- `blocked` — parked, not for the factory
- No label — tracked but not for autonomous work
### 7. Watch it work
### 6. Watch it work
The dev-agent polls every 5 minutes. Trigger manually to see it immediately:
```bash
source .env
export PROJECT_TOML=projects/<name>.toml
docker exec -u agent disinto-agents-1 bash -c "cd /home/agent/disinto && bash dev/dev-poll.sh projects/<name>.toml"
```

View file

@ -30,18 +30,13 @@ if [ -f "$FACTORY_ROOT/.env.enc" ] && command -v sops &>/dev/null; then
_saved_forge_url="${FORGE_URL:-}"
_saved_forge_token="${FORGE_TOKEN:-}"
# Use temp file + validate dotenv format before sourcing (avoids eval injection)
_tmpenv=$(mktemp) || { echo "Warning: failed to create temp file for .env.enc" >&2; exit 1; }
_tmpenv=$(mktemp) || { echo "Warning: failed to create temp file for .env.enc" >&2; }
if sops -d --output-type dotenv "$FACTORY_ROOT/.env.enc" > "$_tmpenv" 2>/dev/null; then
# Validate: non-empty, non-comment lines must match KEY=value pattern
# Filter out blank lines and comments before validation
_validated=$(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$_tmpenv" 2>/dev/null || true)
if [ -n "$_validated" ]; then
# Write validated content to a second temp file and source it
_validated_env=$(mktemp)
printf '%s\n' "$_validated" > "$_validated_env"
# Validate: each non-empty, non-comment line must match KEY=value pattern
if grep -qE '^[A-Za-z_][A-Za-z0-9_]*=' "$_tmpenv" 2>/dev/null && \
! grep -qE '^[^A-Za-z_]' "$_tmpenv" 2>/dev/null; then
# shellcheck source=/dev/null
source "$_validated_env"
rm -f "$_validated_env"
source "$_tmpenv"
else
echo "Warning: .env.enc decryption output failed format validation" >&2
fi

View file

@ -20,9 +20,8 @@ mirror_push() {
if [[ ! "$upper_name" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then
continue
fi
# Use indirect expansion safely (no eval) — MIRROR_ prefix required
local varname="MIRROR_${upper_name}"
url="${!varname:-}"
# Use indirect expansion safely (no eval)
url="${!upper_name:-}"
[ -z "$url" ] && continue
# Ensure remote exists with correct URL