name: CodeQL Security Analysis
on:
push:
branches: [main, develop]
paths:
- "simplenote_mcp/**"
- ".github/workflows/codeql-analysis.yml"
pull_request:
branches: [main]
paths:
- "simplenote_mcp/**"
- ".github/workflows/codeql-analysis.yml"
schedule:
# Run weekly on Sundays at 02:00 UTC
- cron: "0 2 * * 0"
workflow_dispatch:
inputs:
languages:
description: "Languages to analyze (comma-separated: python,javascript)"
required: false
default: "python"
type: string
queries:
description: "Query suite to run"
required: false
default: "default"
type: choice
options:
- default
- security-extended
- security-and-quality
permissions:
actions: read
contents: read
security-events: write
pull-requests: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
name: CodeQL Analysis (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
language: ["python"]
# Add 'javascript' if we have significant JS code
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
# Fetch full history for better analysis
fetch-depth: 0
- name: Set up Python
if: matrix.language == 'python'
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: "pip"
- name: Install Python dependencies
if: matrix.language == 'python'
run: |
python -m pip install --upgrade pip
# Install minimal dependencies needed for analysis
pip install -e ".[dev]" --no-deps
pip install mcp requests simplenote
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# Custom queries for security-focused analysis
config: |
name: "CodeQL Security Analysis"
disable-default-queries: false
queries:
- uses: security-extended
- uses: security-and-quality
paths-ignore:
- "tests/**"
- "docs/**"
- "scripts/**"
- "*.md"
paths:
- "simplenote_mcp/**"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"
upload: true
- name: Process CodeQL Results
if: always()
run: |
echo "🔍 CodeQL Analysis completed for ${{ matrix.language }}"
echo "Results have been uploaded to GitHub Security tab"
# Check if results directory exists and process alerts
if [ -d "${{ runner.temp }}/codeql_results" ]; then
echo "📊 Processing CodeQL results..."
find "${{ runner.temp }}/codeql_results" -name "*.sarif" -exec echo "Found SARIF: {}" \;
fi
security-summary:
name: Security Analysis Summary
runs-on: ubuntu-latest
needs: analyze
if: always()
permissions:
security-events: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Generate security summary
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
// Get CodeQL analysis results - needs.analyze returns { result: 'success'|'failure' }
const analyzeResult = ${{ toJSON(needs.analyze) }};
let summary = "# 🔍 CodeQL Security Analysis Summary\n\n";
summary += `**Workflow**: ${context.workflow}\n`;
summary += `**Run ID**: ${context.runId}\n`;
summary += `**Triggered by**: ${context.eventName}\n\n`;
summary += "## Analysis Results\n\n";
// The analyze job result contains the overall status
const status = analyzeResult.result || 'unknown';
const allSuccessful = status === 'success';
const statusEmoji = allSuccessful ? '✅' : '❌';
// Languages analyzed (from matrix)
const languages = ['python'];
for (const language of languages) {
summary += `- ${statusEmoji} **${language.toUpperCase()}**: ${status.toUpperCase()}\n`;
}
summary += "\n## Security Recommendations\n\n";
if (allSuccessful) {
summary += "- ✅ All CodeQL analyses completed successfully\n";
summary += "- 📊 Review any security alerts in the Security tab\n";
summary += "- 🔄 CodeQL will continue monitoring for new vulnerabilities\n";
} else {
summary += "- ⚠️ Some CodeQL analyses failed - review workflow logs\n";
summary += "- 🔧 Fix any configuration issues and re-run analysis\n";
summary += "- 📋 Ensure all dependencies are properly configured\n";
}
summary += "\n## Next Steps\n\n";
summary += "1. **Review Alerts**: Check the [Security tab](../../security/code-scanning) for any new findings\n";
summary += "2. **Address Issues**: Prioritize and fix any identified security vulnerabilities\n";
summary += "3. **Monitor Trends**: Track security improvements over time\n";
summary += "4. **Update Queries**: Consider expanding analysis with additional query suites\n\n";
summary += "---\n";
summary += "*This summary was automatically generated by the CodeQL Analysis workflow.*\n";
console.log(summary);
// Save summary as artifact
fs.writeFileSync('codeql-summary.md', summary);
// Post to PR if this is a pull request
if (context.eventName === 'pull_request') {
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existingComment = comments.data.find(
comment => comment.body.includes('CodeQL Security Analysis Summary')
);
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body: summary
});
console.log('Updated existing CodeQL summary comment');
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: summary
});
console.log('Created new CodeQL summary comment');
}
}
- name: Upload CodeQL summary
if: always()
uses: actions/upload-artifact@v6
with:
name: codeql-security-summary
path: codeql-summary.md
retention-days: 30
security-monitoring:
name: Security Alert Monitoring
runs-on: ubuntu-latest
needs: analyze
if: always() && github.event_name == 'schedule'
permissions:
security-events: read
issues: write
steps:
- name: Check for new security alerts
uses: actions/github-script@v8
with:
script: |
// Get recent CodeQL alerts
try {
const alerts = await github.rest.codeScanning.listAlertsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100
});
const recentAlerts = alerts.data.filter(alert => {
const createdAt = new Date(alert.created_at);
const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
return createdAt > dayAgo;
});
if (recentAlerts.length > 0) {
console.log(`Found ${recentAlerts.length} new security alerts in the last 24 hours`);
// Create issue for new alerts
const issueBody = [
'## 🚨 New Security Alerts Detected',
'',
`**Alert Count**: ${recentAlerts.length}`,
`**Detection Time**: ${new Date().toISOString()}`,
'**Analysis**: Scheduled CodeQL scan',
'',
'### Recent Alerts:',
...recentAlerts.map(alert =>
`- **${alert.rule.severity.toUpperCase()}**: ${alert.rule.description} (${alert.rule.id})`
),
'',
'### Recommendations:',
'1. Review and prioritize alerts by severity',
'2. Assign alerts to appropriate team members',
'3. Create remediation plan for high/critical alerts',
'4. Update security baseline after fixes',
'',
'**Alert Details**: [Security Tab](../../security/code-scanning)',
'',
'---',
'*This issue was automatically created by CodeQL Security Monitoring*'
].join('\n');
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `🚨 Security Alert: ${recentAlerts.length} new CodeQL findings`,
body: issueBody,
labels: ['security', 'automated', 'codeql']
});
console.log('Created security alert issue');
} else {
console.log('No new security alerts found');
}
} catch (error) {
console.error('Failed to check security alerts:', error);
}