Skip to content

Commit 4d0c428

Browse files
committed
fix(ci): resolve security issues, bugs, and redundancy across CI/CD pipelines
Security fixes: - Fix script injection in performance-consolidated.yml (PR comment via env var) - Fix script injection in release-v2.yml (user input via env var) - Fix token exposure in release-consolidated.yml (use env var instead of CLI arg) - Remove hardcoded RUSTSEC ignore and || true defeating security audit Permissions hardening: - Add missing permissions blocks to ci-unified, ci, cross-platform-validation, performance-optimized, and changelog workflows Bug fixes: - Fix Docker image tag mismatch in docker-improved.yml (use metadata tags) - Fix Windows error test using bash syntax in PowerShell shell - Fix git push auth failure in performance-consolidated.yml (use artifact upload) - Fix mtime-based workflow age check (use git log in CI instead of find -mtime) - Remove dead macOS brew test step running on ubuntu-latest - Fix concurrent cargo builds racing on shared target directory - Fix cross install from unpinned git HEAD (use --locked from crates.io) - Fix release-consolidated.yml bot email to proper GitHub noreply address - Replace dead PR comment step with artifact upload in cross-platform-validation - Use annotated tags in release-consolidated.yml Consolidation (reduce redundant CI runs): - Disable PR/push triggers on quality-consolidated.yml (overlap with ci-optimized) - Disable PR trigger on quality.yml (overlap with ci-optimized) - Disable schedule on testing.yml (duplicate of test-consolidated.yml same time) - Disable PR trigger on tag-after-release-pr.yml (duplicate of release-consolidated) - Disable PR/push triggers on performance-optimized.yml (overlap with ci-optimized) Other improvements: - Add explicit permissions to ci-optimized.yml - Deprecate ci-unified.yml and ci.yml push/PR triggers (keep scheduled/release) - Migrate Semgrep from deprecated action to container-based scan - Relax Cargo.toml tree-sitter version pins to semver-compatible ranges - Add .mcp.json to .gitignore - Fix clippy warning in src/error.rs (use std::io::Error::other)
1 parent 4054919 commit 4d0c428

20 files changed

Lines changed: 175 additions & 143 deletions

.github/workflows/changelog.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ name: Changelog Generation
33
on:
44
workflow_dispatch:
55
push:
6-
branches: [ main ]
6+
branches: [main]
77
paths:
8-
- 'cliff.toml'
9-
- '.github/workflows/changelog.yml'
8+
- "cliff.toml"
9+
- ".github/workflows/changelog.yml"
10+
11+
permissions:
12+
contents: read
1013

1114
jobs:
1215
generate:

.github/workflows/ci-optimized.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ on:
1414
required: false
1515
default: "Manual CI execution"
1616

17+
# Explicit permissions for security best practices
18+
permissions:
19+
contents: read
20+
checks: write
21+
pull-requests: read
22+
1723
# Concurrency controls to prevent redundant runs
1824
concurrency:
1925
group: ${{ github.workflow }}-${{ github.ref }}

.github/workflows/ci-unified.yml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
name: Unified CI/CD Pipeline
22

3+
# DEPRECATED: This workflow is replaced by ci-optimized.yml
4+
# Keeping for scheduled comprehensive testing only
35
on:
4-
push:
5-
branches: [ main ]
6-
pull_request:
7-
branches: [ main ]
8-
types: [ opened, synchronize, reopened ]
6+
# push:
7+
# branches: [ main ]
8+
# pull_request:
9+
# branches: [ main ]
10+
# types: [ opened, synchronize, reopened ]
11+
schedule:
12+
# Run weekly on Sundays at 4 AM UTC
13+
- cron: "0 4 * * 0"
914
workflow_dispatch:
1015

16+
permissions:
17+
contents: read
18+
1119
concurrency:
1220
group: ${{ github.workflow }}-${{ github.ref }}
1321
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

.github/workflows/ci.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
name: CI/CD
22

3+
# DEPRECATED: This workflow is replaced by ci-optimized.yml
4+
# Keeping only for release builds
35
on:
4-
push:
5-
branches: [main, develop]
6-
pull_request:
7-
branches: [main]
6+
# push:
7+
# branches: [main, develop]
8+
# pull_request:
9+
# branches: [main]
810
release:
911
types: [published]
1012
workflow_dispatch:
1113

14+
permissions:
15+
contents: read
16+
1217
# Concurrency control to prevent redundant runs
1318
concurrency:
1419
group: ${{ github.workflow }}-${{ github.ref }}

.github/workflows/cross-platform-validation.yml

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ on:
77
workflow_dispatch:
88
inputs:
99
test_type:
10-
description: 'Type of cross-platform test to run'
10+
description: "Type of cross-platform test to run"
1111
required: true
12-
default: 'full'
12+
default: "full"
1313
type: choice
1414
options:
1515
- full
1616
- quick
1717
- targets-only
1818

19+
permissions:
20+
contents: read
21+
1922
env:
2023
CARGO_TERM_COLOR: always
2124

@@ -78,7 +81,7 @@ jobs:
7881

7982
- name: Install cross (for cross-compilation)
8083
if: matrix.cross == true
81-
run: cargo install cross --git https://github.com/cross-rs/cross
84+
run: cargo install cross --locked
8285

8386
- name: Cache cargo registry
8487
uses: actions/cache@v5
@@ -215,17 +218,20 @@ jobs:
215218
./target/release/batless${{ matrix.binary_extension }} large_test.txt --max-lines=100 --mode=plain > /dev/null
216219
./target/release/batless${{ matrix.binary_extension }} large_test.txt --max-bytes=1024 --mode=plain > /dev/null
217220
218-
- name: Test error handling
219-
shell: ${{ matrix.shell }}
221+
- name: Test error handling (Unix)
222+
if: matrix.os != 'windows-latest'
223+
shell: bash
220224
run: |
221-
# Test non-existent file (cross-platform compatible)
222-
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
223-
./target/release/batless${{ matrix.binary_extension }} nonexistent.txt 2>&1 | findstr /i "error" || echo "Error handling test passed"
224-
else
225-
set +e # Don't fail on expected errors
226-
./target/release/batless${{ matrix.binary_extension }} nonexistent.txt 2>&1 | grep -i "error" || echo "Error handling test passed"
227-
set -e
228-
fi
225+
set +e # Don't fail on expected errors
226+
./target/release/batless nonexistent.txt 2>&1 | grep -i "error" || echo "Error handling test passed"
227+
set -e
228+
229+
- name: Test error handling (Windows)
230+
if: matrix.os == 'windows-latest'
231+
shell: pwsh
232+
run: |
233+
$output = & "./target/release/batless.exe" nonexistent.txt 2>&1 | Out-String
234+
if ($output -match "(?i)error") { Write-Host "Error handling test passed" } else { Write-Host "Error handling test passed (no error string)" }
229235
230236
integration-validation:
231237
name: Integration Validation
@@ -273,7 +279,8 @@ jobs:
273279
name: Cross-Platform Report
274280
runs-on: ubuntu-latest
275281
if: always()
276-
needs: [cross-compile-targets, platform-specific-tests, integration-validation]
282+
needs:
283+
[cross-compile-targets, platform-specific-tests, integration-validation]
277284

278285
steps:
279286
- name: Generate cross-platform report
@@ -306,16 +313,9 @@ jobs:
306313
307314
cat report.md
308315
309-
- name: Comment on PR with results
310-
if: github.event_name == 'pull_request'
311-
uses: actions/github-script@v8
316+
- name: Upload report
317+
uses: actions/upload-artifact@v6
312318
with:
313-
script: |
314-
const fs = require('fs');
315-
const report = fs.readFileSync('report.md', 'utf8');
316-
github.rest.issues.createComment({
317-
issue_number: context.issue.number,
318-
owner: context.repo.owner,
319-
repo: context.repo.repo,
320-
body: report
321-
});
319+
name: cross-platform-report
320+
path: report.md
321+
retention-days: 30

.github/workflows/docker-improved.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ on:
1010
workflow_dispatch:
1111
inputs:
1212
reason:
13-
description: 'Reason for manual trigger'
13+
description: "Reason for manual trigger"
1414
required: false
15-
default: 'Manual testing'
15+
default: "Manual testing"
1616

1717
env:
1818
REGISTRY: ghcr.io
@@ -96,10 +96,12 @@ jobs:
9696
provenance: false
9797

9898
- name: Test Docker image (amd64)
99-
if: always()
99+
if: success() && steps.dyn.outputs.push == 'false'
100100
run: |
101-
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} --version
102-
echo "fn main() { println!(\"Hello\"); }" | docker run --rm -i ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} --language=rust
101+
# Use the first tag from metadata (available locally after build)
102+
IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -1)
103+
docker run --rm "${IMAGE_TAG}" --version
104+
echo "fn main() { println!(\"Hello\"); }" | docker run --rm -i "${IMAGE_TAG}" --language=rust
103105
104106
- name: Generate artifact summary
105107
if: github.event_name != 'pull_request'

.github/workflows/health-check.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Repository Health Check
33
on:
44
schedule:
55
# Run daily at 6 AM UTC
6-
- cron: '0 6 * * *'
6+
- cron: "0 6 * * *"
77
workflow_dispatch:
88

99
permissions:
@@ -46,13 +46,20 @@ jobs:
4646
fi
4747
echo "" >> health-report.md
4848
49-
# Check for outdated workflows
49+
# Check for outdated workflows (using git log, not filesystem mtime)
5050
echo "## Workflow File Ages" >> health-report.md
51-
OLD_WORKFLOWS=$(find .github/workflows -name "*.yml" -mtime +90)
51+
OLD_WORKFLOWS=""
52+
NINETY_DAYS_AGO=$(date -u -d '90 days ago' '+%Y-%m-%d' 2>/dev/null || date -u -v-90d '+%Y-%m-%d')
53+
for wf in .github/workflows/*.yml; do
54+
LAST_MODIFIED=$(git log -1 --format='%ai' -- "$wf" 2>/dev/null | cut -d' ' -f1)
55+
if [ -n "$LAST_MODIFIED" ] && [ "$LAST_MODIFIED" \< "$NINETY_DAYS_AGO" ]; then
56+
OLD_WORKFLOWS="${OLD_WORKFLOWS}${wf} (last modified: ${LAST_MODIFIED})\n"
57+
fi
58+
done
5259
if [ -n "$OLD_WORKFLOWS" ]; then
5360
echo "⚠️ Workflows not updated in 90+ days:" >> health-report.md
5461
echo '```' >> health-report.md
55-
echo "$OLD_WORKFLOWS" >> health-report.md
62+
echo -e "$OLD_WORKFLOWS" >> health-report.md
5663
echo '```' >> health-report.md
5764
ISSUES_FOUND=$((ISSUES_FOUND + 1))
5865
else

.github/workflows/performance-consolidated.yml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ on:
2323
workflow_dispatch:
2424
inputs:
2525
update_baseline:
26-
description: 'Update performance baseline'
26+
description: "Update performance baseline"
2727
required: false
2828
type: boolean
2929
default: false
@@ -233,14 +233,13 @@ jobs:
233233
cp benchmark_results.md .github/performance/baseline.md
234234
echo "updated=true" >> "$GITHUB_OUTPUT"
235235
236-
# If we have git changes, commit them
237-
if ! git diff --quiet .github/performance/; then
238-
git config --local user.email "action@github.com"
239-
git config --local user.name "GitHub Action"
240-
git add .github/performance/
241-
git commit -m "Update performance baseline [skip ci]"
242-
git push
243-
fi
236+
- name: Upload baseline artifact
237+
if: steps.baseline.outputs.updated == 'true'
238+
uses: actions/upload-artifact@v6
239+
with:
240+
name: performance-baseline
241+
path: .github/performance/baseline.md
242+
retention-days: 90
244243

245244
comment-pr:
246245
name: Comment on PR
@@ -250,13 +249,15 @@ jobs:
250249
steps:
251250
- name: Comment PR
252251
uses: actions/github-script@v8
252+
env:
253+
PR_COMMENT: ${{ needs.benchmark.outputs.pr-comment }}
253254
with:
254255
script: |
255256
github.rest.issues.createComment({
256257
issue_number: context.issue.number,
257258
owner: context.repo.owner,
258259
repo: context.repo.repo,
259-
body: `${{ needs.benchmark.outputs.pr-comment }}`
260+
body: process.env.PR_COMMENT
260261
})
261262
262263
create-issue:

.github/workflows/performance-optimized.yml

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
name: Performance Optimized CI
22

3+
# PR/push triggers disabled to avoid overlap with ci-optimized.yml
4+
# Use workflow_dispatch for manual performance-focused CI runs
35
on:
4-
# Ultra-fast performance CI for development workflow
5-
pull_request:
6-
types: [opened, synchronize, reopened]
7-
push:
8-
branches: [develop]
6+
# pull_request:
7+
# types: [opened, synchronize, reopened]
8+
# push:
9+
# branches: [develop]
910
workflow_dispatch:
1011
inputs:
1112
test_reason:
1213
description: "Reason for performance testing"
1314
required: false
1415
default: "Performance CI execution"
1516

17+
permissions:
18+
contents: read
19+
1620
# Aggressive concurrency to cancel outdated runs
1721
concurrency:
1822
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
@@ -149,21 +153,14 @@ jobs:
149153
with:
150154
save-if: false
151155

152-
- name: Parallel checks
153-
run: |
154-
# Run multiple checks in parallel
155-
cargo clippy --no-deps -- -W clippy::all &
156-
clippy_pid=$!
157-
158-
cargo check --all-features &
159-
check_pid=$!
156+
- name: Run clippy
157+
run: cargo clippy --no-deps -- -W clippy::all
160158

161-
# Documentation check
162-
cargo doc --no-deps --all-features &
163-
doc_pid=$!
159+
- name: Check all features
160+
run: cargo check --all-features
164161

165-
# Wait for all
166-
wait $clippy_pid $check_pid $doc_pid
162+
- name: Documentation check
163+
run: cargo doc --no-deps --all-features
167164

168165
# Minimal security check
169166
security-quick:
@@ -178,9 +175,7 @@ jobs:
178175
- uses: dtolnay/rust-toolchain@stable
179176

180177
- name: Quick audit
181-
run: |
182-
# Only check for critical vulnerabilities
183-
cargo audit --deny warnings --ignore RUSTSEC-2020-0071 || true
178+
run: cargo audit
184179

185180
# Final aggregation
186181
ci-complete:

.github/workflows/quality-consolidated.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
name: Code Quality & Security
22

3+
# CONSOLIDATED: PR/push triggers disabled to avoid overlap with ci-optimized.yml
4+
# Kept for scheduled weekly security/quality checks only
35
on:
4-
pull_request:
5-
branches: [main]
6-
push:
7-
branches: [main]
6+
# pull_request:
7+
# branches: [main]
8+
# push:
9+
# branches: [main]
810
schedule:
911
# Run security checks weekly
1012
- cron: "0 2 * * 1"
@@ -28,7 +30,7 @@ jobs:
2830
- name: Setup Node.js
2931
uses: actions/setup-node@v6
3032
with:
31-
node-version: '20'
33+
node-version: "20"
3234

3335
- name: Install markdownlint-cli2
3436
run: npm install -g markdownlint-cli2@0.14.0

0 commit comments

Comments
 (0)