fix(skills): keep curl credentials out of argv (#2175)

* fix(skills): avoid curl credential argv leaks

* test(ci): guard secret curl examples
This commit is contained in:
Kumario 2026-06-07 00:25:45 -05:00 committed by GitHub
parent 40673a89fa
commit 70fde3c14f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 143 additions and 19 deletions

View File

@ -65,6 +65,15 @@ MCP が利用できない場合は、`curl` またはヘルパースクリプト
シェル環境変数、シークレットマネージャー、またはリポジトリにコミットしないローカル環境ファイルに保存してください。 シェル環境変数、シークレットマネージャー、またはリポジトリにコミットしないローカル環境ファイルに保存してください。
直接 `curl` 例では、Jira ユーザー設定を標準入力で渡し、認証情報がコマンドライン引数に出ないようにします。
```bash
jira_curl() {
printf 'user = "%s:%s"\n' "$JIRA_EMAIL" "$JIRA_API_TOKEN" |
curl -s -K - "$@"
}
```
## MCP ツールリファレンス ## MCP ツールリファレンス
`mcp-atlassian` MCP サーバーが設定されている場合、以下のツールが利用可能です。 `mcp-atlassian` MCP サーバーが設定されている場合、以下のツールが利用可能です。
@ -88,7 +97,7 @@ MCP が利用できない場合は、`curl` またはヘルパースクリプト
### チケットの取得 ### チケットの取得
```bash ```bash
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234" | jq '{ "$JIRA_URL/rest/api/3/issue/PROJ-1234" | jq '{
key: .key, key: .key,
@ -105,7 +114,7 @@ curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### コメントの取得 ### コメントの取得
```bash ```bash
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234?fields=comment" | jq '.fields.comment.comments[] | { "$JIRA_URL/rest/api/3/issue/PROJ-1234?fields=comment" | jq '.fields.comment.comments[] | {
author: .author.displayName, author: .author.displayName,
@ -117,7 +126,7 @@ curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### コメントの追加 ### コメントの追加
```bash ```bash
curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{ -d '{
"body": { "body": {
@ -136,11 +145,11 @@ curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
```bash ```bash
# 1. 利用可能なトランジションを取得 # 1. 利用可能なトランジションを取得
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" | jq '.transitions[] | {id, name: .name}' "$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" | jq '.transitions[] | {id, name: .name}'
# 2. トランジションを実行TRANSITION_ID を置き換える) # 2. トランジションを実行TRANSITION_ID を置き換える)
curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"transition": {"id": "TRANSITION_ID"}}' \ -d '{"transition": {"id": "TRANSITION_ID"}}' \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" "$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions"
@ -149,7 +158,7 @@ curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### JQL での検索 ### JQL での検索
```bash ```bash
curl -s -G -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -G \
--data-urlencode "jql=project = PROJ AND status = 'In Progress'" \ --data-urlencode "jql=project = PROJ AND status = 'In Progress'" \
"$JIRA_URL/rest/api/3/search" "$JIRA_URL/rest/api/3/search"
``` ```

View File

@ -67,6 +67,15 @@ origin: ECC
将这些存储在您的 shell 环境、密钥管理器或未跟踪的本地环境文件中。不要将其提交到仓库。 将这些存储在您的 shell 环境、密钥管理器或未跟踪的本地环境文件中。不要将其提交到仓库。
对于直接 `curl` 示例,请通过标准输入传递 Jira 用户配置,避免凭据出现在命令行参数中。
```bash
jira_curl() {
printf 'user = "%s:%s"\n' "$JIRA_EMAIL" "$JIRA_API_TOKEN" |
curl -s -K - "$@"
}
```
## MCP 工具参考 ## MCP 工具参考
当配置了 `mcp-atlassian` MCP 服务器时,以下工具可用: 当配置了 `mcp-atlassian` MCP 服务器时,以下工具可用:
@ -90,7 +99,7 @@ origin: ECC
### 获取工单 ### 获取工单
```bash ```bash
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234" | jq '{ "$JIRA_URL/rest/api/3/issue/PROJ-1234" | jq '{
key: .key, key: .key,
@ -107,7 +116,7 @@ curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### 获取评论 ### 获取评论
```bash ```bash
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234?fields=comment" | jq '.fields.comment.comments[] | { "$JIRA_URL/rest/api/3/issue/PROJ-1234?fields=comment" | jq '.fields.comment.comments[] | {
author: .author.displayName, author: .author.displayName,
@ -119,7 +128,7 @@ curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### 添加评论 ### 添加评论
```bash ```bash
curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{ -d '{
"body": { "body": {
@ -138,11 +147,11 @@ curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
```bash ```bash
# 1. Get available transitions # 1. Get available transitions
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" | jq '.transitions[] | {id, name: .name}' "$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" | jq '.transitions[] | {id, name: .name}'
# 2. Execute transition (replace TRANSITION_ID) # 2. Execute transition (replace TRANSITION_ID)
curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"transition": {"id": "TRANSITION_ID"}}' \ -d '{"transition": {"id": "TRANSITION_ID"}}' \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" "$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions"
@ -151,7 +160,7 @@ curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### 使用 JQL 搜索 ### 使用 JQL 搜索
```bash ```bash
curl -s -G -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -G \
--data-urlencode "jql=project = PROJ AND status = 'In Progress'" \ --data-urlencode "jql=project = PROJ AND status = 'In Progress'" \
"$JIRA_URL/rest/api/3/search" "$JIRA_URL/rest/api/3/search"
``` ```

View File

@ -65,6 +65,15 @@ If MCP is not available, use the Jira REST API v3 directly via `curl` or a helpe
Store these in your shell environment, secrets manager, or an untracked local env file. Do not commit them to the repo. Store these in your shell environment, secrets manager, or an untracked local env file. Do not commit them to the repo.
For direct `curl` examples, keep credentials out of command-line arguments by passing the Jira user config on stdin:
```bash
jira_curl() {
printf 'user = "%s:%s"\n' "$JIRA_EMAIL" "$JIRA_API_TOKEN" |
curl -s -K - "$@"
}
```
## MCP Tools Reference ## MCP Tools Reference
When the `mcp-atlassian` MCP server is configured, these tools are available: When the `mcp-atlassian` MCP server is configured, these tools are available:
@ -88,7 +97,7 @@ When the `mcp-atlassian` MCP server is configured, these tools are available:
### Fetch a Ticket ### Fetch a Ticket
```bash ```bash
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234" | jq '{ "$JIRA_URL/rest/api/3/issue/PROJ-1234" | jq '{
key: .key, key: .key,
@ -105,7 +114,7 @@ curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### Fetch Comments ### Fetch Comments
```bash ```bash
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234?fields=comment" | jq '.fields.comment.comments[] | { "$JIRA_URL/rest/api/3/issue/PROJ-1234?fields=comment" | jq '.fields.comment.comments[] | {
author: .author.displayName, author: .author.displayName,
@ -117,7 +126,7 @@ curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### Add a Comment ### Add a Comment
```bash ```bash
curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{ -d '{
"body": { "body": {
@ -136,11 +145,11 @@ curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
```bash ```bash
# 1. Get available transitions # 1. Get available transitions
curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" | jq '.transitions[] | {id, name: .name}' "$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" | jq '.transitions[] | {id, name: .name}'
# 2. Execute transition (replace TRANSITION_ID) # 2. Execute transition (replace TRANSITION_ID)
curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"transition": {"id": "TRANSITION_ID"}}' \ -d '{"transition": {"id": "TRANSITION_ID"}}' \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" "$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions"
@ -149,7 +158,7 @@ curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
### Search with JQL ### Search with JQL
```bash ```bash
curl -s -G -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \ jira_curl -G \
--data-urlencode "jql=project = PROJ AND status = 'In Progress'" \ --data-urlencode "jql=project = PROJ AND status = 'In Progress'" \
"$JIRA_URL/rest/api/3/search" "$JIRA_URL/rest/api/3/search"
``` ```

View File

@ -23,7 +23,8 @@ Connects Claude Code to [SocialClaw](https://getsocialclaw.com) for agent-driven
export SC_API_KEY="<workspace-key>" export SC_API_KEY="<workspace-key>"
# Verify access # Verify access
curl -sS -H "Authorization: Bearer $SC_API_KEY" https://getsocialclaw.com/v1/keys/validate printf 'header = "Authorization: Bearer %s"\n' "$SC_API_KEY" |
curl -sS -K - https://getsocialclaw.com/v1/keys/validate
# Install CLI (optional but recommended) # Install CLI (optional but recommended)
npm install -g socialclaw@0.1.12 npm install -g socialclaw@0.1.12

View File

@ -0,0 +1,96 @@
#!/usr/bin/env node
/**
* Guard agent-facing curl examples from exposing credentials in argv.
*/
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const repoRoot = path.resolve(__dirname, '..', '..');
const jiraDocs = [
'skills/jira-integration/SKILL.md',
'docs/ja-JP/skills/jira-integration/SKILL.md',
'docs/zh-CN/skills/jira-integration/SKILL.md',
];
const socialDocs = [
'skills/social-publisher/SKILL.md',
];
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (error) {
console.log(`${name}`);
console.log(` Error: ${error.message}`);
return false;
}
}
function read(relativePath) {
return fs.readFileSync(path.join(repoRoot, relativePath), 'utf8');
}
function shellExamples(source) {
const examples = [];
const fencePattern = /```(?:bash|sh|shell)\r?\n([\s\S]*?)```/g;
let match;
while ((match = fencePattern.exec(source)) !== null) {
examples.push(match[1].replace(/\\\r?\n\s*/g, ' '));
}
return examples.join('\n');
}
function run() {
console.log('\n=== Testing secret-safe curl examples ===\n');
let passed = 0;
let failed = 0;
for (const relativePath of jiraDocs) {
if (test(`${relativePath} keeps Jira credentials out of curl argv`, () => {
const source = read(relativePath);
const shell = shellExamples(source);
assert.match(shell, /jira_curl\(\)/, 'Expected a Jira curl wrapper');
assert.match(shell, /\bcurl -s -K - "\$@"/, 'Expected curl config stdin in Jira wrapper');
assert.doesNotMatch(
shell,
/\bcurl\b[^\n]*(?:-u|--user)(?:=|\s+)(?:"|')?\$JIRA_EMAIL:\$JIRA_API_TOKEN/,
'Jira credentials must not be passed with curl -u/--user',
);
})) passed++; else failed++;
}
for (const relativePath of socialDocs) {
if (test(`${relativePath} keeps SocialClaw bearer token out of curl argv`, () => {
const source = read(relativePath);
const shell = shellExamples(source);
assert.match(
shell,
/printf 'header = "Authorization: Bearer %s"\\n' "\$SC_API_KEY" \|/,
'Expected SocialClaw bearer header to be passed via curl config stdin',
);
assert.match(shell, /\bcurl -sS -K - https:\/\/getsocialclaw\.com\/v1\/keys\/validate/, 'Expected curl -K - validation call');
assert.doesNotMatch(
shell,
/\bcurl\b[^\n]*-H\s+(?:"|')Authorization:\s*Bearer\s+\$SC_API_KEY(?:"|')/,
'SocialClaw bearer token must not be passed with curl -H',
);
})) passed++; else failed++;
}
console.log(`\nPassed: ${passed}`);
console.log(`Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);
}
run();