Skip to content

Commit 7a74939

Browse files
committed
Harden shell scripts and fix stale docs from CodeRabbit review
- Replace eval tilde expansion with getent passwd lookup in all PR-scoped scripts (setup-auth, setup-migrate, ccstatusline, mcp-qdrant install + poststart hook) to prevent shell injection via SUDO_USER/USER environment variables - JSON-escape auth token value before writing .credentials.json - Create credential directory with umask 077 (was default 755) - Fix mcp-qdrant chown to use resolved _USERNAME instead of hardcoded vscode or $(id -un) - Update ccstatusline README verification commands to respect CLAUDE_CONFIG_DIR environment variable - Sync docs site changelog with devcontainer CHANGELOG fixes (~/. claude path, remove "(leading dot)" aside)
1 parent 6a8ba62 commit 7a74939

8 files changed

Lines changed: 33 additions & 14 deletions

File tree

.devcontainer/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@
1616

1717
#### Security
1818
- Protected-files-guard now blocks modifications to `.credentials.json`
19+
- Replaced `eval` tilde expansion with `getent passwd` lookup across all scripts (prevents shell injection via `SUDO_USER`/`USER`)
20+
- Auth token value is now JSON-escaped before writing to `.credentials.json`
21+
- Credential directory created with restrictive umask (700) matching credential file permissions (600)
1922

2023
#### Scripts
2124
- Replaced `setup-symlink-claude.sh` with `setup-migrate-claude.sh` (one-time migration)
2225
- Auto-migrates from `/workspaces/.claude/` if `.credentials.json` present
26+
- `chown` in mcp-qdrant poststart hooks now uses resolved `_USERNAME` instead of hardcoded `vscode` or `$(id -un)`
2327

2428
#### Documentation
2529
- All docs now reference `~/.claude` as default config path
2630
- Added `CLAUDE_AUTH_TOKEN` setup flow to README, configuration reference, and troubleshooting
31+
- ccstatusline README verification commands now respect `CLAUDE_CONFIG_DIR`
2732

2833
### Removed
2934

.devcontainer/features/ccstatusline/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ You should see formatted output with powerline styling.
106106

107107
**3. Check Claude Code integration:**
108108
```bash
109-
cat ~/.claude/settings.json | jq '.statusLine'
109+
cat "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" | jq '.statusLine'
110110
```
111111

112112
Should show:
@@ -205,7 +205,7 @@ cat ~/.config/ccstatusline/settings.json | jq .
205205
echo '{"model":{"display_name":"Test"}}' | npx -y ccstatusline@latest
206206

207207
# 3. Check Claude Code settings
208-
cat ~/.claude/settings.json | jq '.statusLine'
208+
cat "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" | jq '.statusLine'
209209

210210
# 4. Manually run auto-config if needed
211211
configure-ccstatusline-auto
@@ -259,7 +259,7 @@ configure-ccstatusline-auto
259259
npm install -g ccstatusline@latest
260260
```
261261

262-
Then update `~/.claude/settings.json`:
262+
Then update `${CLAUDE_CONFIG_DIR:-~/.claude}/settings.json`:
263263
```json
264264
{
265265
"statusLine": {

.devcontainer/features/ccstatusline/install.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ fi
192192
193193
# Use SUDO_USER since _REMOTE_USER isn't set in post-start hooks
194194
USERNAME="${SUDO_USER:-vscode}"
195-
_USER_HOME=$(eval echo "~${USERNAME}")
195+
_USER_HOME=$(getent passwd "$USERNAME" 2>/dev/null | cut -d: -f6)
196+
_USER_HOME="${_USER_HOME:-/home/$USERNAME}"
196197
SETTINGS_FILE="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}/settings.json"
197198
198199
# Ensure directory exists

.devcontainer/features/mcp-qdrant/install.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ fi
190190
191191
# Resolve target user's home (guards against $HOME=/root during feature install)
192192
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
193-
_USER_HOME=$(eval echo "~${_USERNAME}")
193+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
194+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
194195
195196
# Ensure settings.json exists
196197
SETTINGS_FILE="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}/settings.json"
@@ -261,7 +262,7 @@ fi
261262
262263
# Set proper permissions
263264
chmod 644 "$SETTINGS_FILE"
264-
chown "$(id -un):$(id -gn)" "$SETTINGS_FILE" 2>/dev/null || true
265+
chown "${_USERNAME}:${_USERNAME}" "$SETTINGS_FILE" 2>/dev/null || true
265266
266267
echo "[mcp-qdrant] ✓ Configuration complete"
267268
HOOK_EOF

.devcontainer/features/mcp-qdrant/poststart-hook.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ fi
5858

5959
# Resolve target user's home (guards against $HOME=/root when hook runs as root)
6060
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
61-
_USER_HOME=$(eval echo "~${_USERNAME}")
61+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
62+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
6263

6364
# Ensure settings.json exists
6465
SETTINGS_FILE="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}/settings.json"
@@ -129,6 +130,6 @@ fi
129130

130131
# Set proper permissions
131132
chmod 644 "$SETTINGS_FILE"
132-
chown vscode:vscode "$SETTINGS_FILE" 2>/dev/null || true
133+
chown "${_USERNAME}:${_USERNAME}" "$SETTINGS_FILE" 2>/dev/null || true
133134

134135
echo "[mcp-qdrant] ✓ Configuration complete"

.devcontainer/scripts/setup-auth.sh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ fi
7070
# Note: After unset, the token remains visible in /proc/<pid>/environ for the
7171
# lifetime of this process. This is a platform limitation of environment variables.
7272
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
73-
_USER_HOME=$(eval echo "~${_USERNAME}")
73+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
74+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
7475
CLAUDE_CRED_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}"
7576
CLAUDE_CRED_FILE="$CLAUDE_CRED_DIR/.credentials.json"
7677
if [ -n "$CLAUDE_AUTH_TOKEN" ]; then
@@ -88,11 +89,15 @@ if [ -n "$CLAUDE_AUTH_TOKEN" ]; then
8889
AUTH_CONFIGURED=true
8990
else
9091
echo "[setup-auth] Creating .credentials.json from CLAUDE_AUTH_TOKEN..."
91-
mkdir -p "$CLAUDE_CRED_DIR"
92+
# Create directory with restrictive permissions (matches credential file at 600)
93+
( umask 077; mkdir -p "$CLAUDE_CRED_DIR" )
94+
# Escape JSON-special characters in token value (defense against malformed JSON
95+
# if a token ever contains " or \ — unlikely with sk-ant-* but closes the gap)
96+
ESCAPED_TOKEN=$(printf '%s' "$CLAUDE_AUTH_TOKEN" | sed 's/\\/\\\\/g; s/"/\\"/g')
9297
# Write credentials with restrictive permissions from the start (no race window).
9398
# Uses printf '%s' to avoid shell expansion of token value (defense against
9499
# metacharacters in the token string — backticks, $(), quotes).
95-
if ( umask 077; printf '{\n "claudeAiOauth": {\n "accessToken": "%s",\n "refreshToken": "%s",\n "expiresAt": 9999999999999,\n "scopes": ["user:inference", "user:profile"]\n }\n}\n' "$CLAUDE_AUTH_TOKEN" "$CLAUDE_AUTH_TOKEN" > "$CLAUDE_CRED_FILE" ); then
100+
if ( umask 077; printf '{\n "claudeAiOauth": {\n "accessToken": "%s",\n "refreshToken": "%s",\n "expiresAt": 9999999999999,\n "scopes": ["user:inference", "user:profile"]\n }\n}\n' "$ESCAPED_TOKEN" "$ESCAPED_TOKEN" > "$CLAUDE_CRED_FILE" ); then
96101
echo "[setup-auth] Claude auth token configured"
97102
AUTH_CONFIGURED=true
98103
else

.devcontainer/scripts/setup-migrate-claude.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
OLD_DIR="/workspaces/.claude"
1010
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
11-
_USER_HOME=$(eval echo "~${_USERNAME}")
11+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
12+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
1213
NEW_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}"
1314

1415
# Nothing to migrate if old directory doesn't exist

docs/src/content/docs/reference/changelog.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,28 @@ For minor and patch updates, you can usually just rebuild the container. Check t
5454
#### Configuration
5555
- Moved `.claude` directory from `/workspaces/.claude` to `~/.claude` (home directory)
5656
- Added Docker named volume for persistence across rebuilds (per-instance isolation via `${devcontainerId}`)
57-
- `CLAUDE_CONFIG_DIR` now defaults to `/home/vscode/.claude`
57+
- `CLAUDE_CONFIG_DIR` now defaults to `~/.claude`
5858

5959
#### Authentication
6060
- Added `CLAUDE_AUTH_TOKEN` support in `.secrets` for long-lived tokens from `claude setup-token`
6161
- Auto-creates `.credentials.json` from token on container start (skips if already exists)
6262
- Added `CLAUDE_AUTH_TOKEN` to devcontainer.json secrets declaration
6363

6464
#### Security
65-
- Protected-files-guard now covers `.credentials.json` (leading dot)
65+
- Protected-files-guard now blocks modifications to `.credentials.json`
66+
- Replaced `eval` tilde expansion with `getent passwd` lookup across all scripts (prevents shell injection via `SUDO_USER`/`USER`)
67+
- Auth token value is now JSON-escaped before writing to `.credentials.json`
68+
- Credential directory created with restrictive umask (700) matching credential file permissions (600)
6669

6770
#### Scripts
6871
- Replaced `setup-symlink-claude.sh` with `setup-migrate-claude.sh` (one-time migration)
6972
- Auto-migrates from `/workspaces/.claude/` if `.credentials.json` present
73+
- `chown` in mcp-qdrant poststart hooks now uses resolved `_USERNAME` instead of hardcoded `vscode` or `$(id -un)`
7074

7175
#### Documentation
7276
- All docs now reference `~/.claude` as default config path
7377
- Added `CLAUDE_AUTH_TOKEN` setup flow to README, configuration reference, and troubleshooting
78+
- ccstatusline README verification commands now respect `CLAUDE_CONFIG_DIR`
7479

7580
### Removed
7681

0 commit comments

Comments
 (0)