diff --git a/external_plugins/discord/server.ts b/external_plugins/discord/server.ts index f34006a..078c29a 100644 --- a/external_plugins/discord/server.ts +++ b/external_plugins/discord/server.ts @@ -25,7 +25,7 @@ import { type Attachment, } from 'discord.js' import { randomBytes } from 'crypto' -import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync, renameSync, realpathSync } from 'fs' +import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync, renameSync, realpathSync, chmodSync } from 'fs' import { homedir } from 'os' import { join, sep } from 'path' @@ -37,6 +37,8 @@ const ENV_FILE = join(STATE_DIR, '.env') // Load ~/.claude/channels/discord/.env into process.env. Real env wins. // Plugin-spawned servers don't get an env block — this is where the token lives. try { + // Token is a credential — lock to owner. No-op on Windows (would need ACLs). + chmodSync(ENV_FILE, 0o600) for (const line of readFileSync(ENV_FILE, 'utf8').split('\n')) { const m = line.match(/^(\w+)=(.*)$/) if (m && process.env[m[1]] === undefined) process.env[m[1]] = m[2] diff --git a/external_plugins/discord/skills/configure/SKILL.md b/external_plugins/discord/skills/configure/SKILL.md index b6d8df0..a1e15f8 100644 --- a/external_plugins/discord/skills/configure/SKILL.md +++ b/external_plugins/discord/skills/configure/SKILL.md @@ -80,7 +80,8 @@ as the correct long-term choice. Don't skip the lockdown offer. 2. `mkdir -p ~/.claude/channels/discord` 3. Read existing `.env` if present; update/add the `DISCORD_BOT_TOKEN=` line, preserve other keys. Write back, no quotes around the value. -4. Confirm, then show the no-args status so the user sees where they stand. +4. `chmod 600 ~/.claude/channels/discord/.env` — the token is a credential. +5. Confirm, then show the no-args status so the user sees where they stand. ### `clear` — remove the token diff --git a/external_plugins/telegram/server.ts b/external_plugins/telegram/server.ts index e2265b8..8acd52a 100644 --- a/external_plugins/telegram/server.ts +++ b/external_plugins/telegram/server.ts @@ -18,7 +18,7 @@ import { import { Bot, InputFile, type Context } from 'grammy' import type { ReactionTypeEmoji } from 'grammy/types' import { randomBytes } from 'crypto' -import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync, renameSync, realpathSync } from 'fs' +import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync, renameSync, realpathSync, chmodSync } from 'fs' import { homedir } from 'os' import { join, extname, sep } from 'path' @@ -30,6 +30,8 @@ const ENV_FILE = join(STATE_DIR, '.env') // Load ~/.claude/channels/telegram/.env into process.env. Real env wins. // Plugin-spawned servers don't get an env block — this is where the token lives. try { + // Token is a credential — lock to owner. No-op on Windows (would need ACLs). + chmodSync(ENV_FILE, 0o600) for (const line of readFileSync(ENV_FILE, 'utf8').split('\n')) { const m = line.match(/^(\w+)=(.*)$/) if (m && process.env[m[1]] === undefined) process.env[m[1]] = m[2] diff --git a/external_plugins/telegram/skills/configure/SKILL.md b/external_plugins/telegram/skills/configure/SKILL.md index 3d846cf..31ad2f3 100644 --- a/external_plugins/telegram/skills/configure/SKILL.md +++ b/external_plugins/telegram/skills/configure/SKILL.md @@ -77,7 +77,8 @@ offer. 2. `mkdir -p ~/.claude/channels/telegram` 3. Read existing `.env` if present; update/add the `TELEGRAM_BOT_TOKEN=` line, preserve other keys. Write back, no quotes around the value. -4. Confirm, then show the no-args status so the user sees where they stand. +4. `chmod 600 ~/.claude/channels/telegram/.env` — the token is a credential. +5. Confirm, then show the no-args status so the user sees where they stand. ### `clear` — remove the token