Skip to content

Commit 0e06493

Browse files
authored
security: Harden CI/CD and repo for open-source safety (#458)
* enhance: Improve prompt-engineer agent (automated review) - Fix broken reasoning framework: complete Ordering field and Necessity scale - Remove OpenAI attribution from description; add invocation example block - Restrict tools to Read only (text-transformation agent needs no writes) - Add XML Structure and CoT Opportunity dimensions to reasoning template - Move meta-instruction NOTE out of output template, place as agent directive - Fix stray markdown bold markers on Reasoning Before Conclusions guideline - Integrate orphaned examples bullet into the Examples guideline line - Add concrete worked example section (classify customer feedback) Automated review cycle | Co-Authored-By: Claude Code <noreply@anthropic.com> * security: Harden CI/CD and repo for open-source safety - Add --ignore-scripts to all npm ci/install in CI workflows and predeploy script - Fix shell injection in discord-release-notification (use env vars + jq for payload) - Add explicit permissions to all workflows (least privilege principle) - Add root .npmrc with ignore-scripts=true - Add .github/dependabot.yml for automated dependency security updates - Add .github/CODEOWNERS to protect workflows, scripts, and API paths - Expand .gitignore with *.pem, *.key, credentials.json patterns * fix: Grant contents:read to deploy workflow so checkout works Cubic correctly flagged that permissions: {} blocks actions/checkout.
1 parent 29bb81c commit 0e06493

10 files changed

Lines changed: 107 additions & 68 deletions

.github/CODEOWNERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Protect CI/CD and security-sensitive paths
2+
.github/ @danipower
3+
scripts/ @danipower
4+
.npmrc @danipower
5+
CLAUDE.md @danipower
6+
dashboard/src/pages/api/ @danipower

.github/dependabot.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "npm"
4+
directory: "/cli-tool"
5+
schedule:
6+
interval: "weekly"
7+
open-pull-requests-limit: 5
8+
labels:
9+
- "dependencies"
10+
11+
- package-ecosystem: "npm"
12+
directory: "/dashboard"
13+
schedule:
14+
interval: "weekly"
15+
open-pull-requests-limit: 5
16+
labels:
17+
- "dependencies"
18+
19+
- package-ecosystem: "github-actions"
20+
directory: "/"
21+
schedule:
22+
interval: "weekly"
23+
open-pull-requests-limit: 5
24+
labels:
25+
- "ci"

.github/workflows/component-security-validation.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ on:
1212
paths:
1313
- 'cli-tool/components/**/*.md'
1414

15+
permissions:
16+
contents: read
17+
pull-requests: write
18+
1519
jobs:
1620
security-audit:
1721
name: Security Audit
@@ -33,7 +37,7 @@ jobs:
3337
- name: Install dependencies
3438
run: |
3539
cd cli-tool
36-
npm ci
40+
npm ci --ignore-scripts
3741
3842
- name: Run Security Audit
3943
id: audit

.github/workflows/deploy.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
paths:
77
- 'dashboard/**'
88

9+
permissions:
10+
contents: read
11+
912
concurrency:
1013
group: deploy-${{ github.ref }}
1114
cancel-in-progress: false

.github/workflows/discord-release-notification.yml

Lines changed: 50 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ on:
44
release:
55
types: [published]
66

7+
permissions:
8+
contents: read
9+
710
jobs:
811
notify-discord:
912
name: Send Discord Notification
@@ -12,83 +15,67 @@ jobs:
1215
steps:
1316
- name: Send Discord Webhook
1417
env:
18+
RELEASE_NAME: ${{ github.event.release.name }}
19+
RELEASE_TAG: ${{ github.event.release.tag_name }}
20+
RELEASE_URL: ${{ github.event.release.html_url }}
21+
RELEASE_AUTHOR: ${{ github.event.release.author.login }}
22+
RELEASE_AVATAR: ${{ github.event.release.author.avatar_url }}
23+
RELEASE_DATE: ${{ github.event.release.published_at }}
1524
RELEASE_BODY: ${{ github.event.release.body }}
25+
REPO_NAME: ${{ github.repository }}
26+
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
1627
run: |
17-
# Prepare the Discord embed
18-
RELEASE_NAME="${{ github.event.release.name }}"
19-
RELEASE_TAG="${{ github.event.release.tag_name }}"
20-
RELEASE_URL="${{ github.event.release.html_url }}"
21-
RELEASE_AUTHOR="${{ github.event.release.author.login }}"
22-
RELEASE_AVATAR="${{ github.event.release.author.avatar_url }}"
23-
RELEASE_DATE="${{ github.event.release.published_at }}"
24-
REPO_NAME="${{ github.repository }}"
25-
2628
# Truncate body if too long (Discord limit is 4096 characters for description)
27-
# Read from environment variable to prevent command execution
2829
TRUNCATED_BODY=$(printf '%s' "$RELEASE_BODY" | head -c 2000)
2930
BODY_LENGTH=$(printf '%s' "$RELEASE_BODY" | wc -c | tr -d ' ')
3031
if [ "$BODY_LENGTH" -gt 2000 ]; then
31-
TRUNCATED_BODY="${TRUNCATED_BODY}...\n\n[Read more]($RELEASE_URL)"
32+
TRUNCATED_BODY="${TRUNCATED_BODY}...\n\n[Read more](${RELEASE_URL})"
3233
fi
3334
34-
# Escape special characters for JSON using jq
35-
TRUNCATED_BODY=$(printf '%s' "$TRUNCATED_BODY" | jq -Rs .)
36-
RELEASE_NAME=$(printf '%s' "$RELEASE_NAME" | jq -Rs .)
37-
38-
# Create Discord webhook payload
39-
PAYLOAD=$(cat <<EOF
40-
{
41-
"username": "GitHub Release Bot",
42-
"avatar_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
43-
"embeds": [
44-
{
45-
"title": "🚀 New Release: $RELEASE_TAG",
46-
"description": $TRUNCATED_BODY,
47-
"url": "$RELEASE_URL",
48-
"color": 5814783,
49-
"fields": [
50-
{
51-
"name": "📦 Version",
52-
"value": "$RELEASE_TAG",
53-
"inline": true
35+
# Build payload safely with jq (no shell interpolation in JSON)
36+
PAYLOAD=$(jq -n \
37+
--arg tag "$RELEASE_TAG" \
38+
--arg body "$TRUNCATED_BODY" \
39+
--arg url "$RELEASE_URL" \
40+
--arg author "$RELEASE_AUTHOR" \
41+
--arg repo "$REPO_NAME" \
42+
--arg date "$RELEASE_DATE" \
43+
--arg avatar "$RELEASE_AVATAR" \
44+
'{
45+
username: "GitHub Release Bot",
46+
avatar_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
47+
embeds: [
48+
{
49+
title: ("🚀 New Release: " + $tag),
50+
description: $body,
51+
url: $url,
52+
color: 5814783,
53+
fields: [
54+
{ name: "📦 Version", value: $tag, inline: true },
55+
{ name: "👤 Author", value: $author, inline: true },
56+
{ name: "📚 Repository", value: ("[\($repo)](https://github.com/\($repo))"), inline: false },
57+
{ name: "📥 Installation", value: "```bash\nnpm install -g claude-code-templates\n# or with npx\nnpx claude-code-templates@latest --help\n```", inline: false }
58+
],
59+
footer: {
60+
text: "GitHub Release Notification",
61+
icon_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
5462
},
55-
{
56-
"name": "👤 Author",
57-
"value": "$RELEASE_AUTHOR",
58-
"inline": true
59-
},
60-
{
61-
"name": "📚 Repository",
62-
"value": "[$REPO_NAME](https://github.com/$REPO_NAME)",
63-
"inline": false
64-
},
65-
{
66-
"name": "📥 Installation",
67-
"value": "\`\`\`bash\nnpm install -g claude-code-templates\n# or with npx\nnpx claude-code-templates@latest --help\n\`\`\`",
68-
"inline": false
69-
}
70-
],
71-
"footer": {
72-
"text": "GitHub Release Notification",
73-
"icon_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
74-
},
75-
"timestamp": "$RELEASE_DATE",
76-
"thumbnail": {
77-
"url": "$RELEASE_AVATAR"
63+
timestamp: $date,
64+
thumbnail: { url: $avatar }
7865
}
79-
}
80-
]
81-
}
82-
EOF
83-
)
66+
]
67+
}')
8468
8569
# Send webhook
86-
curl -H "Content-Type: application/json" \
70+
curl -sf -H "Content-Type: application/json" \
8771
-d "$PAYLOAD" \
88-
"${{ secrets.DISCORD_WEBHOOK_URL }}"
72+
"$DISCORD_WEBHOOK_URL"
8973
9074
- name: Verify notification
75+
env:
76+
RELEASE_TAG: ${{ github.event.release.tag_name }}
77+
RELEASE_URL: ${{ github.event.release.html_url }}
9178
run: |
9279
echo "✅ Discord notification sent successfully!"
93-
echo "Release: ${{ github.event.release.tag_name }}"
94-
echo "URL: ${{ github.event.release.html_url }}"
80+
echo "Release: $RELEASE_TAG"
81+
echo "URL: $RELEASE_URL"

.github/workflows/publish-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
4646
- name: Install dependencies
4747
working-directory: ./cli-tool
48-
run: npm ci
48+
run: npm ci --ignore-scripts
4949

5050
- name: Run tests
5151
working-directory: ./cli-tool

.github/workflows/update-json-data.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
- cron: '0 3 * * *'
77
workflow_dispatch: # Permite ejecutar manualmente desde GitHub UI
88

9+
permissions:
10+
contents: write
11+
912
jobs:
1013
update-json:
1114
runs-on: ubuntu-latest
@@ -37,7 +40,7 @@ jobs:
3740
- name: Install Node.js dependencies
3841
run: |
3942
cd cli-tool
40-
npm ci
43+
npm ci --ignore-scripts
4144
4245
- name: Create .env file with secrets
4346
run: |

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,13 @@ openspec/
7171

7272
# Crawl output (cf-crawl skill)
7373
.crawl-output/
74+
75+
# Secrets and credentials
76+
*.pem
77+
*.key
78+
*.p12
79+
*.pfx
80+
credentials.json
81+
service-account*.json
82+
*-credentials.json
83+
.npmrc.local

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ignore-scripts=true

scripts/predeploy-check.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ echo "-----------------------------------------"
9696
# Install root dependencies
9797
if [ -f "package.json" ]; then
9898
info "Installing root dependencies..."
99-
if npm install --silent; then
99+
if npm install --silent --ignore-scripts; then
100100
success "Root dependencies installed"
101101
else
102102
error "Failed to install root dependencies"
@@ -108,7 +108,7 @@ fi
108108
if [ -f "api/package.json" ]; then
109109
info "Installing API dependencies..."
110110
cd api
111-
if npm install --silent; then
111+
if npm install --silent --ignore-scripts; then
112112
success "API dependencies installed"
113113
else
114114
error "Failed to install API dependencies"

0 commit comments

Comments
 (0)