Skip to content

Commit cb8bc70

Browse files
committed
feat: improve config validation, add AGENTS.md guidelines
- Use nameref arrays for safer error handling in config helpers - Add AGENTS.md with structure, coding, and security guidelines - Streamline atl binary install in Dockerfile - Add TOCTOU note in config loader
1 parent 434745e commit cb8bc70

3 files changed

Lines changed: 61 additions & 15 deletions

File tree

AGENTS.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
- Root CLI: `claude.sh` (main wrapper), `claude-yolo` (YOLO alias).
5+
- Container: `Dockerfile`, `Makefile` for build/test tasks.
6+
- CI/CD: `.github/workflows/*` (CI, release, Claude bot).
7+
- Scripts: `scripts/version-check.sh` (version consistency).
8+
- Docs & examples: `README.md`, `CHANGELOG.md`, `workflows/`, `examples/`.
9+
- Config: project `.claude-yolo` and `.claude-yolo.local` (gitignored); user config under `~/.config/claude-yolo/`.
10+
11+
## Build, Test, and Development Commands
12+
- `make build`: Build Docker image (`CLAUDE_CODE_VERSION` overridable).
13+
- `make rebuild|buildx|buildx-multi`: Rebuild (no cache) or multi‑arch.
14+
- `make test`: Smoke tests (CLI help/version, toolchain check).
15+
- `make lint`: Lint `Dockerfile` (hadolint).
16+
- `make version-check`: Ensure `claude.sh`, tags, and `CHANGELOG.md` align.
17+
- Run locally: `./claude.sh --yolo` or `./claude-yolo` in your project dir.
18+
19+
## Coding Style & Naming Conventions
20+
- Language: Bash. Start scripts with `#!/bin/bash` and `set -e` (consider `-u -o pipefail` when safe).
21+
- Security: validate inputs; avoid backticks/eval; reject path traversal; prefer read‑only mounts.
22+
- Naming: scripts `*.sh`; CLI helpers kebab‑case; constants `ALL_CAPS`, vars/functions `snake_case`.
23+
- Output: short, clear; mask secrets (follow `mask_sensitive_value`).
24+
25+
## Testing Guidelines
26+
- Linting: CI runs ShellCheck. Locally run ShellCheck if installed.
27+
- CI: GitHub Actions runs help/version checks and version consistency.
28+
- Local: `make test` before PR; keep tests minimal and behavior‑focused.
29+
- Versioning: update `VERSION` in `claude.sh` and `CHANGELOG.md`; run `scripts/version-check.sh`.
30+
31+
## Commit & Pull Request Guidelines
32+
- Conventional Commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:` (see `git log`).
33+
- PRs: clear description, linked issues, rationale for security‑sensitive changes, `make test` output.
34+
- Docs: update README/CHANGELOG when user‑visible behavior or flags change.
35+
- Releases: use tags (`vX.Y.Z`) and the release workflow; multi‑arch images are pushed from CI.
36+
37+
## Security & Configuration Tips
38+
- Never run YOLO in `$HOME` or system dirs; always `cd` into a project.
39+
- Validate `-v` mounts; avoid `..` traversal; keep secrets read‑only.
40+
- Prefer project config: `.claude-yolo`; sensitive overrides in `.claude-yolo.local`.
41+
- Auth via env when needed: `ANTHROPIC_API_KEY`, `CLAUDE_CODE_OAUTH_TOKEN`, `GH_TOKEN`.
42+

Dockerfile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ RUN npm config set prefix "$CLAUDE_HOME/.npm-global" && \
144144
npm cache clean --force
145145

146146
# Install Go tools for Atlassian integration (Confluence/Jira/Bitbucket)
147-
# Build as claude user; move binary as root later (avoid sudo in build)
148-
RUN go install github.com/lroolle/atlas-cli/cmd/atl@main
147+
RUN go install github.com/lroolle/atlas-cli/cmd/atl@main && \
148+
sudo mv $HOME/go/bin/atl /usr/local/bin/
149149

150150
RUN git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh "$CLAUDE_HOME/.oh-my-zsh" && \
151151
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$CLAUDE_HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" && \
@@ -160,9 +160,6 @@ RUN echo 'export ZSH="$HOME/.oh-my-zsh"' > "$CLAUDE_HOME/.zshrc" && \
160160

161161
USER root
162162

163-
# Move atl into PATH as root (after building as claude)
164-
RUN test -f /home/claude/go/bin/atl && mv /home/claude/go/bin/atl /usr/local/bin/atl || true
165-
166163
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
167164

168165
RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \

claude.sh

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,12 @@ validate_config_value() {
356356
process_volume_config() {
357357
local value="$1"
358358
local errors_var="$2"
359+
declare -n errors_ref="$errors_var"
359360

360361
value="${value//\"/}"
361362

362363
if ! validate_config_value "VOLUME" "$value"; then
363-
eval "$errors_var+=(\"Invalid volume: \$value\")"
364+
errors_ref+=("Invalid volume: $value")
364365
return 1
365366
fi
366367

@@ -372,18 +373,19 @@ process_volume_config() {
372373
EXTRA_VOLUMES+=("-v" "$value")
373374
DOCKER_ONLY_WARNINGS+=("Config volume mount: $value (ignored in local mode)")
374375
else
375-
eval "$errors_var+=(\"Volume validation failed: \$value\")"
376+
errors_ref+=("Volume validation failed: $value")
376377
fi
377378
}
378379

379380
process_env_config() {
380381
local value="$1"
381382
local errors_var="$2"
383+
declare -n errors_ref="$errors_var"
382384

383385
value="${value//\"/}"
384386

385387
if ! validate_config_value "ENV" "$value"; then
386-
eval "$errors_var+=(\"Invalid env: \$value\")"
388+
errors_ref+=("Invalid env: $value")
387389
return 1
388390
fi
389391

@@ -394,8 +396,7 @@ process_env_config() {
394396
EXTRA_ENV_VARS+=("-e" "$name=$val")
395397
DOCKER_ONLY_WARNINGS+=("Config environment variable: $name=$val (ignored in local mode)")
396398
else
397-
eval "$errors_var+=(\"Invalid env name: \$name\")"
398-
fi
399+
errors_ref+=("Invalid env name: $name")
399400
else
400401
# Shorthand pass-through: ENV=${VAR} or ENV=$VAR
401402
if [[ "$value" =~ ^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$ ]] || [[ "$value" =~ ^\$([A-Za-z_][A-Za-z0-9_]*)$ ]]; then
@@ -417,16 +418,17 @@ process_var_config() {
417418
local name="$1"
418419
local value="$2"
419420
local errors_var="$3"
421+
declare -n errors_ref="$errors_var"
420422

421423
if ! [[ "$name" =~ ^[A-Z][A-Z0-9_]*$ ]]; then
422-
eval "$errors_var+=(\"Invalid variable name: \$name\")"
424+
errors_ref+=("Invalid variable name: $name")
423425
return 1
424426
fi
425427

426428
value="${value//\"/}"
427429

428430
if ! validate_config_value "$name" "$value"; then
429-
eval "$errors_var+=(\"Validation failed for \$name=\$value\")"
431+
errors_ref+=("Validation failed for $name=$value")
430432
return 1
431433
fi
432434

@@ -439,14 +441,14 @@ process_var_config() {
439441
CONFIG_DIR="$value"
440442
if [ ! -d "$CONFIG_DIR" ]; then
441443
mkdir -p "$CONFIG_DIR" 2>/dev/null || {
442-
eval "$errors_var+=(\"Cannot create CONFIG_DIR: \$CONFIG_DIR\")"
444+
errors_ref+=("Cannot create CONFIG_DIR: $CONFIG_DIR")
443445
CONFIG_DIR=""
444446
return 1
445447
}
446448
fi
447449
if [ -n "$CONFIG_DIR" ] && [ ! -f "$CONFIG_DIR/.claude.json" ]; then
448450
echo '{}' >"$CONFIG_DIR/.claude.json" 2>/dev/null || {
449-
eval "$errors_var+=(\"Cannot create \$CONFIG_DIR/.claude.json\")"
451+
errors_ref+=("Cannot create $CONFIG_DIR/.claude.json")
450452
}
451453
fi
452454
;;
@@ -479,7 +481,7 @@ process_var_config() {
479481
if [[ "$name" =~ ^(DISABLE_|MAX_|ANTHROPIC_|CLAUDE_|AWS_|GOOGLE_) ]]; then
480482
export "$name"="$value"
481483
else
482-
eval "$errors_var+=(\"Unknown config variable: \$name\")"
484+
errors_ref+=("Unknown config variable: $name")
483485
fi
484486
;;
485487
esac
@@ -489,6 +491,11 @@ load_config_file() {
489491
local config_file="$1"
490492
[ -f "$config_file" ] || return 1
491493

494+
# Note: There's a potential TOCTOU race condition here where the config file
495+
# could be modified between validation and loading. For a fully atomic solution,
496+
# we would need to read the file once into memory and process from there.
497+
# However, given the nature of config files (user-controlled, local),
498+
# the security impact is minimal and the current approach is pragmatic.
492499
local errors=()
493500

494501
while IFS= read -r line || [ -n "$line" ]; do

0 commit comments

Comments
 (0)