Initial commit
This commit is contained in:
9
skills/appsec/api-spectral/assets/.gitkeep
Normal file
9
skills/appsec/api-spectral/assets/.gitkeep
Normal file
@@ -0,0 +1,9 @@
|
||||
# Assets Directory
|
||||
|
||||
Place files that will be used in the output Claude produces:
|
||||
- Templates
|
||||
- Configuration files
|
||||
- Images/logos
|
||||
- Boilerplate code
|
||||
|
||||
These files are NOT loaded into context but copied/modified in output.
|
||||
357
skills/appsec/api-spectral/assets/ci-config-template.yml
Normal file
357
skills/appsec/api-spectral/assets/ci-config-template.yml
Normal file
@@ -0,0 +1,357 @@
|
||||
# Security-Enhanced CI/CD Pipeline Template
|
||||
#
|
||||
# This template demonstrates security best practices for CI/CD pipelines.
|
||||
# Adapt this template to your specific security tool and workflow needs.
|
||||
#
|
||||
# Key Security Features:
|
||||
# - SAST (Static Application Security Testing)
|
||||
# - Dependency vulnerability scanning
|
||||
# - Secrets detection
|
||||
# - Infrastructure-as-Code security scanning
|
||||
# - Container image scanning
|
||||
# - Security artifact uploading for compliance
|
||||
|
||||
name: Security Scan Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
schedule:
|
||||
# Run weekly security scans on Sunday at 2 AM UTC
|
||||
- cron: '0 2 * * 0'
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
# Security: Restrict permissions to minimum required
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write # For uploading SARIF results
|
||||
pull-requests: write # For commenting on PRs
|
||||
|
||||
env:
|
||||
# Configuration
|
||||
SECURITY_SCAN_FAIL_ON: 'critical,high' # Fail build on these severities
|
||||
REPORT_DIR: 'security-reports'
|
||||
|
||||
jobs:
|
||||
# Job 1: Static Application Security Testing (SAST)
|
||||
sast-scan:
|
||||
name: SAST Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Full history for better analysis
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Run SAST Scanner
|
||||
run: |
|
||||
# Example: Using Semgrep for SAST
|
||||
pip install semgrep
|
||||
semgrep --config=auto \
|
||||
--json \
|
||||
--output ${{ env.REPORT_DIR }}/sast-results.json \
|
||||
. || true
|
||||
|
||||
# Alternative: Bandit for Python projects
|
||||
# pip install bandit
|
||||
# bandit -r . -f json -o ${{ env.REPORT_DIR }}/bandit-results.json
|
||||
|
||||
- name: Process SAST Results
|
||||
run: |
|
||||
# Parse results and fail on critical/high severity
|
||||
python3 -c "
|
||||
import json
|
||||
import sys
|
||||
|
||||
with open('${{ env.REPORT_DIR }}/sast-results.json') as f:
|
||||
results = json.load(f)
|
||||
|
||||
critical = len([r for r in results.get('results', []) if r.get('extra', {}).get('severity') == 'ERROR'])
|
||||
high = len([r for r in results.get('results', []) if r.get('extra', {}).get('severity') == 'WARNING'])
|
||||
|
||||
print(f'Critical findings: {critical}')
|
||||
print(f'High findings: {high}')
|
||||
|
||||
if critical > 0:
|
||||
print('❌ Build failed: Critical security issues found')
|
||||
sys.exit(1)
|
||||
elif high > 0:
|
||||
print('⚠️ Warning: High severity issues found')
|
||||
# Optionally fail on high severity
|
||||
# sys.exit(1)
|
||||
else:
|
||||
print('✅ No critical security issues found')
|
||||
"
|
||||
|
||||
- name: Upload SAST Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sast-results
|
||||
path: ${{ env.REPORT_DIR }}/sast-results.json
|
||||
retention-days: 30
|
||||
|
||||
# Job 2: Dependency Vulnerability Scanning
|
||||
dependency-scan:
|
||||
name: Dependency Vulnerability Scan
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Scan Python Dependencies
|
||||
if: hashFiles('requirements.txt') != ''
|
||||
run: |
|
||||
pip install safety
|
||||
safety check \
|
||||
--json \
|
||||
--output ${{ env.REPORT_DIR }}/safety-results.json \
|
||||
|| true
|
||||
|
||||
- name: Scan Node Dependencies
|
||||
if: hashFiles('package.json') != ''
|
||||
run: |
|
||||
npm audit --json > ${{ env.REPORT_DIR }}/npm-audit.json || true
|
||||
|
||||
- name: Process Dependency Results
|
||||
run: |
|
||||
# Check for critical vulnerabilities
|
||||
if [ -f "${{ env.REPORT_DIR }}/safety-results.json" ]; then
|
||||
critical_count=$(python3 -c "import json; data=json.load(open('${{ env.REPORT_DIR }}/safety-results.json')); print(len([v for v in data.get('vulnerabilities', []) if v.get('severity', '').lower() == 'critical']))")
|
||||
echo "Critical vulnerabilities: $critical_count"
|
||||
if [ "$critical_count" -gt "0" ]; then
|
||||
echo "❌ Build failed: Critical vulnerabilities in dependencies"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Upload Dependency Scan Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dependency-scan-results
|
||||
path: ${{ env.REPORT_DIR }}/
|
||||
retention-days: 30
|
||||
|
||||
# Job 3: Secrets Detection
|
||||
secrets-scan:
|
||||
name: Secrets Detection
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Full history to scan all commits
|
||||
|
||||
- name: Run Gitleaks
|
||||
uses: gitleaks/gitleaks-action@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITLEAKS_ENABLE_SUMMARY: true
|
||||
|
||||
- name: Alternative - TruffleHog Scan
|
||||
if: false # Set to true to enable
|
||||
run: |
|
||||
pip install truffleHog
|
||||
trufflehog --json --regex --entropy=True . \
|
||||
> ${{ env.REPORT_DIR }}/trufflehog-results.json || true
|
||||
|
||||
- name: Upload Secrets Scan Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: secrets-scan-results
|
||||
path: ${{ env.REPORT_DIR }}/
|
||||
retention-days: 30
|
||||
|
||||
# Job 4: Container Image Scanning
|
||||
container-scan:
|
||||
name: Container Image Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
if: hashFiles('Dockerfile') != ''
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build Docker Image
|
||||
run: |
|
||||
docker build -t app:${{ github.sha }} .
|
||||
|
||||
- name: Run Trivy Scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: app:${{ github.sha }}
|
||||
format: 'sarif'
|
||||
output: '${{ env.REPORT_DIR }}/trivy-results.sarif'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
- name: Upload Trivy Results to GitHub Security
|
||||
if: always()
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: '${{ env.REPORT_DIR }}/trivy-results.sarif'
|
||||
|
||||
- name: Upload Container Scan Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: container-scan-results
|
||||
path: ${{ env.REPORT_DIR }}/
|
||||
retention-days: 30
|
||||
|
||||
# Job 5: Infrastructure-as-Code Security Scanning
|
||||
iac-scan:
|
||||
name: IaC Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
if: hashFiles('**/*.tf', '**/*.yaml', '**/*.yml') != ''
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Checkov
|
||||
run: |
|
||||
pip install checkov
|
||||
checkov -d . \
|
||||
--output json \
|
||||
--output-file ${{ env.REPORT_DIR }}/checkov-results.json \
|
||||
--quiet \
|
||||
|| true
|
||||
|
||||
- name: Run tfsec (for Terraform)
|
||||
if: hashFiles('**/*.tf') != ''
|
||||
run: |
|
||||
curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash
|
||||
tfsec . \
|
||||
--format json \
|
||||
--out ${{ env.REPORT_DIR }}/tfsec-results.json \
|
||||
|| true
|
||||
|
||||
- name: Process IaC Results
|
||||
run: |
|
||||
# Fail on critical findings
|
||||
if [ -f "${{ env.REPORT_DIR }}/checkov-results.json" ]; then
|
||||
critical_count=$(python3 -c "import json; data=json.load(open('${{ env.REPORT_DIR }}/checkov-results.json')); print(data.get('summary', {}).get('failed', 0))")
|
||||
echo "Failed checks: $critical_count"
|
||||
if [ "$critical_count" -gt "0" ]; then
|
||||
echo "⚠️ Warning: IaC security issues found"
|
||||
# Optionally fail the build
|
||||
# exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Upload IaC Scan Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: iac-scan-results
|
||||
path: ${{ env.REPORT_DIR }}/
|
||||
retention-days: 30
|
||||
|
||||
# Job 6: Security Report Generation and Notification
|
||||
security-report:
|
||||
name: Generate Security Report
|
||||
runs-on: ubuntu-latest
|
||||
needs: [sast-scan, dependency-scan, secrets-scan]
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download All Scan Results
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: all-results/
|
||||
|
||||
- name: Generate Consolidated Report
|
||||
run: |
|
||||
# Consolidate all security scan results
|
||||
mkdir -p consolidated-report
|
||||
|
||||
cat > consolidated-report/security-summary.md << 'EOF'
|
||||
# Security Scan Summary
|
||||
|
||||
**Scan Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||
**Commit**: ${{ github.sha }}
|
||||
**Branch**: ${{ github.ref_name }}
|
||||
|
||||
## Scan Results
|
||||
|
||||
### SAST Scan
|
||||
See artifacts: `sast-results`
|
||||
|
||||
### Dependency Scan
|
||||
See artifacts: `dependency-scan-results`
|
||||
|
||||
### Secrets Scan
|
||||
See artifacts: `secrets-scan-results`
|
||||
|
||||
### Container Scan
|
||||
See artifacts: `container-scan-results`
|
||||
|
||||
### IaC Scan
|
||||
See artifacts: `iac-scan-results`
|
||||
|
||||
---
|
||||
|
||||
For detailed results, download scan artifacts from this workflow run.
|
||||
EOF
|
||||
|
||||
- name: Comment on PR (if applicable)
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const report = fs.readFileSync('consolidated-report/security-summary.md', 'utf8');
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: report
|
||||
});
|
||||
|
||||
- name: Upload Consolidated Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: consolidated-security-report
|
||||
path: consolidated-report/
|
||||
retention-days: 90
|
||||
|
||||
# Security Best Practices Demonstrated:
|
||||
#
|
||||
# 1. ✅ Minimal permissions (principle of least privilege)
|
||||
# 2. ✅ Multiple security scan types (defense in depth)
|
||||
# 3. ✅ Fail-fast on critical findings
|
||||
# 4. ✅ Secrets detection across full git history
|
||||
# 5. ✅ Container image scanning before deployment
|
||||
# 6. ✅ IaC scanning for misconfigurations
|
||||
# 7. ✅ Artifact retention for compliance audit trail
|
||||
# 8. ✅ SARIF format for GitHub Security integration
|
||||
# 9. ✅ Scheduled scans for continuous monitoring
|
||||
# 10. ✅ PR comments for developer feedback
|
||||
#
|
||||
# Compliance Mappings:
|
||||
# - SOC 2: CC6.1, CC6.6, CC7.2 (Security monitoring and logging)
|
||||
# - PCI-DSS: 6.2, 6.5 (Secure development practices)
|
||||
# - NIST: SA-11 (Developer Security Testing)
|
||||
# - OWASP: Integrated security testing throughout SDLC
|
||||
189
skills/appsec/api-spectral/assets/github-actions-template.yml
Normal file
189
skills/appsec/api-spectral/assets/github-actions-template.yml
Normal file
@@ -0,0 +1,189 @@
|
||||
# GitHub Actions Workflow for Spectral API Security Linting
|
||||
# This workflow validates OpenAPI/AsyncAPI specifications against security best practices
|
||||
|
||||
name: API Security Linting with Spectral
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- 'api-specs/**/*.yaml'
|
||||
- 'api-specs/**/*.yml'
|
||||
- 'api-specs/**/*.json'
|
||||
- '.spectral.yaml'
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- 'api-specs/**/*.yaml'
|
||||
- 'api-specs/**/*.yml'
|
||||
- 'api-specs/**/*.json'
|
||||
- '.spectral.yaml'
|
||||
|
||||
jobs:
|
||||
spectral-lint:
|
||||
name: Lint API Specifications
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Spectral CLI
|
||||
run: npm install -g @stoplight/spectral-cli
|
||||
|
||||
- name: Verify Spectral Installation
|
||||
run: spectral --version
|
||||
|
||||
- name: Lint API Specifications (GitHub Actions Format)
|
||||
run: |
|
||||
spectral lint api-specs/**/*.{yaml,yml,json} \
|
||||
--ruleset .spectral.yaml \
|
||||
--format github-actions \
|
||||
--fail-severity error
|
||||
continue-on-error: true
|
||||
|
||||
- name: Generate JSON Report
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p reports
|
||||
spectral lint api-specs/**/*.{yaml,yml,json} \
|
||||
--ruleset .spectral.yaml \
|
||||
--format json \
|
||||
--output reports/spectral-results.json || true
|
||||
|
||||
- name: Generate HTML Report
|
||||
if: always()
|
||||
run: |
|
||||
# Download parse script if not in repository
|
||||
if [ ! -f "scripts/parse_spectral_results.py" ]; then
|
||||
curl -o scripts/parse_spectral_results.py \
|
||||
https://raw.githubusercontent.com/SecOpsAgentKit/skills/main/appsec/api-spectral/scripts/parse_spectral_results.py
|
||||
chmod +x scripts/parse_spectral_results.py
|
||||
fi
|
||||
|
||||
python3 scripts/parse_spectral_results.py \
|
||||
--input reports/spectral-results.json \
|
||||
--output reports/spectral-report.html \
|
||||
--format html \
|
||||
--map-owasp
|
||||
|
||||
- name: Upload Spectral Reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spectral-security-reports
|
||||
path: reports/
|
||||
retention-days: 30
|
||||
|
||||
- name: Check for Critical Issues
|
||||
run: |
|
||||
if [ -f "reports/spectral-results.json" ]; then
|
||||
CRITICAL_COUNT=$(jq '[.[] | select(.severity == 0)] | length' reports/spectral-results.json)
|
||||
echo "Critical security issues found: $CRITICAL_COUNT"
|
||||
|
||||
if [ "$CRITICAL_COUNT" -gt 0 ]; then
|
||||
echo "::error::Found $CRITICAL_COUNT critical security issues in API specifications"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Comment PR with Results
|
||||
if: github.event_name == 'pull_request' && always()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
if (fs.existsSync('reports/spectral-results.json')) {
|
||||
const results = JSON.parse(fs.readFileSync('reports/spectral-results.json', 'utf8'));
|
||||
|
||||
const severityCounts = results.reduce((acc, finding) => {
|
||||
const severity = ['error', 'warn', 'info', 'hint'][finding.severity] || 'unknown';
|
||||
acc[severity] = (acc[severity] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const errorCount = severityCounts.error || 0;
|
||||
const warnCount = severityCounts.warn || 0;
|
||||
const infoCount = severityCounts.info || 0;
|
||||
|
||||
const summary = `## 🔒 API Security Lint Results
|
||||
|
||||
**Total Findings:** ${results.length}
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| 🔴 Error | ${errorCount} |
|
||||
| 🟡 Warning | ${warnCount} |
|
||||
| 🔵 Info | ${infoCount} |
|
||||
|
||||
${errorCount > 0 ? '⚠️ **Action Required:** Fix error-level security issues before merging.' : '✅ No critical security issues found.'}
|
||||
|
||||
📄 [View Detailed Report](../actions/runs/${context.runId})
|
||||
`;
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: summary
|
||||
});
|
||||
}
|
||||
|
||||
# Optional: Separate job for OWASP-specific validation
|
||||
owasp-validation:
|
||||
name: OWASP API Security Validation
|
||||
runs-on: ubuntu-latest
|
||||
needs: spectral-lint
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install Spectral CLI
|
||||
run: npm install -g @stoplight/spectral-cli
|
||||
|
||||
- name: Validate Against OWASP Ruleset
|
||||
run: |
|
||||
# Use OWASP-specific ruleset if available
|
||||
if [ -f ".spectral-owasp.yaml" ]; then
|
||||
RULESET=".spectral-owasp.yaml"
|
||||
else
|
||||
# Download OWASP ruleset template
|
||||
curl -o .spectral-owasp.yaml \
|
||||
https://raw.githubusercontent.com/SecOpsAgentKit/skills/main/appsec/api-spectral/assets/spectral-owasp.yaml
|
||||
RULESET=".spectral-owasp.yaml"
|
||||
fi
|
||||
|
||||
spectral lint api-specs/**/*.{yaml,yml,json} \
|
||||
--ruleset "$RULESET" \
|
||||
--format stylish \
|
||||
--fail-severity warn
|
||||
|
||||
- name: Generate OWASP Compliance Report
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p reports
|
||||
spectral lint api-specs/**/*.{yaml,yml,json} \
|
||||
--ruleset .spectral-owasp.yaml \
|
||||
--format json \
|
||||
--output reports/owasp-validation.json || true
|
||||
|
||||
- name: Upload OWASP Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: owasp-compliance-report
|
||||
path: reports/owasp-validation.json
|
||||
retention-days: 30
|
||||
355
skills/appsec/api-spectral/assets/rule-template.yaml
Normal file
355
skills/appsec/api-spectral/assets/rule-template.yaml
Normal file
@@ -0,0 +1,355 @@
|
||||
# Security Rule Template
|
||||
#
|
||||
# This template demonstrates how to structure security rules/policies.
|
||||
# Adapt this template to your specific security tool (Semgrep, OPA, etc.)
|
||||
#
|
||||
# Rule Structure Best Practices:
|
||||
# - Clear rule ID and metadata
|
||||
# - Severity classification
|
||||
# - Framework mappings (OWASP, CWE)
|
||||
# - Remediation guidance
|
||||
# - Example vulnerable and fixed code
|
||||
|
||||
rules:
|
||||
# Example Rule 1: SQL Injection Detection
|
||||
- id: sql-injection-string-concatenation
|
||||
metadata:
|
||||
name: "SQL Injection via String Concatenation"
|
||||
description: "Detects potential SQL injection vulnerabilities from string concatenation in SQL queries"
|
||||
severity: "HIGH"
|
||||
category: "security"
|
||||
subcategory: "injection"
|
||||
|
||||
# Security Framework Mappings
|
||||
owasp:
|
||||
- "A03:2021 - Injection"
|
||||
cwe:
|
||||
- "CWE-89: SQL Injection"
|
||||
mitre_attack:
|
||||
- "T1190: Exploit Public-Facing Application"
|
||||
|
||||
# Compliance Standards
|
||||
compliance:
|
||||
- "PCI-DSS 6.5.1: Injection flaws"
|
||||
- "NIST 800-53 SI-10: Information Input Validation"
|
||||
|
||||
# Confidence and Impact
|
||||
confidence: "HIGH"
|
||||
likelihood: "HIGH"
|
||||
impact: "HIGH"
|
||||
|
||||
# References
|
||||
references:
|
||||
- "https://owasp.org/www-community/attacks/SQL_Injection"
|
||||
- "https://cwe.mitre.org/data/definitions/89.html"
|
||||
- "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html"
|
||||
|
||||
# Languages this rule applies to
|
||||
languages:
|
||||
- python
|
||||
- javascript
|
||||
- java
|
||||
- go
|
||||
|
||||
# Detection Pattern (example using Semgrep-style syntax)
|
||||
pattern-either:
|
||||
- pattern: |
|
||||
cursor.execute($SQL + $VAR)
|
||||
- pattern: |
|
||||
cursor.execute(f"... {$VAR} ...")
|
||||
- pattern: |
|
||||
cursor.execute("..." + $VAR + "...")
|
||||
|
||||
# What to report when found
|
||||
message: |
|
||||
Potential SQL injection vulnerability detected. SQL query is constructed using
|
||||
string concatenation or f-strings with user input. This allows attackers to
|
||||
inject malicious SQL code.
|
||||
|
||||
Use parameterized queries instead:
|
||||
- Python: cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
|
||||
- JavaScript: db.query("SELECT * FROM users WHERE id = $1", [userId])
|
||||
|
||||
See: https://owasp.org/www-community/attacks/SQL_Injection
|
||||
|
||||
# Suggested fix (auto-fix if supported)
|
||||
fix: |
|
||||
Use parameterized queries with placeholders
|
||||
|
||||
# Example vulnerable code
|
||||
examples:
|
||||
- vulnerable: |
|
||||
# Vulnerable: String concatenation
|
||||
user_id = request.GET['id']
|
||||
query = "SELECT * FROM users WHERE id = " + user_id
|
||||
cursor.execute(query)
|
||||
|
||||
- fixed: |
|
||||
# Fixed: Parameterized query
|
||||
user_id = request.GET['id']
|
||||
query = "SELECT * FROM users WHERE id = ?"
|
||||
cursor.execute(query, (user_id,))
|
||||
|
||||
# Example Rule 2: Hardcoded Secrets Detection
|
||||
- id: hardcoded-secret-credential
|
||||
metadata:
|
||||
name: "Hardcoded Secret or Credential"
|
||||
description: "Detects hardcoded secrets, API keys, passwords, or tokens in source code"
|
||||
severity: "CRITICAL"
|
||||
category: "security"
|
||||
subcategory: "secrets"
|
||||
|
||||
owasp:
|
||||
- "A07:2021 - Identification and Authentication Failures"
|
||||
cwe:
|
||||
- "CWE-798: Use of Hard-coded Credentials"
|
||||
- "CWE-259: Use of Hard-coded Password"
|
||||
|
||||
compliance:
|
||||
- "PCI-DSS 8.2.1: Use of strong cryptography"
|
||||
- "SOC 2 CC6.1: Logical access controls"
|
||||
- "GDPR Article 32: Security of processing"
|
||||
|
||||
confidence: "MEDIUM"
|
||||
likelihood: "HIGH"
|
||||
impact: "CRITICAL"
|
||||
|
||||
references:
|
||||
- "https://cwe.mitre.org/data/definitions/798.html"
|
||||
- "https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_password"
|
||||
|
||||
languages:
|
||||
- python
|
||||
- javascript
|
||||
- java
|
||||
- go
|
||||
- ruby
|
||||
|
||||
pattern-either:
|
||||
- pattern: |
|
||||
password = "..."
|
||||
- pattern: |
|
||||
api_key = "..."
|
||||
- pattern: |
|
||||
secret = "..."
|
||||
- pattern: |
|
||||
token = "..."
|
||||
|
||||
pattern-not: |
|
||||
$VAR = ""
|
||||
|
||||
message: |
|
||||
Potential hardcoded secret detected. Hardcoding credentials in source code
|
||||
is a critical security vulnerability that can lead to unauthorized access
|
||||
if the code is exposed.
|
||||
|
||||
Use environment variables or a secrets management system instead:
|
||||
- Python: os.environ.get('API_KEY')
|
||||
- Node.js: process.env.API_KEY
|
||||
- Secrets Manager: AWS Secrets Manager, HashiCorp Vault, etc.
|
||||
|
||||
See: https://cwe.mitre.org/data/definitions/798.html
|
||||
|
||||
examples:
|
||||
- vulnerable: |
|
||||
# Vulnerable: Hardcoded API key
|
||||
api_key = "sk-1234567890abcdef"
|
||||
api.authenticate(api_key)
|
||||
|
||||
- fixed: |
|
||||
# Fixed: Environment variable
|
||||
import os
|
||||
api_key = os.environ.get('API_KEY')
|
||||
if not api_key:
|
||||
raise ValueError("API_KEY environment variable not set")
|
||||
api.authenticate(api_key)
|
||||
|
||||
# Example Rule 3: XSS via Unsafe HTML Rendering
|
||||
- id: xss-unsafe-html-rendering
|
||||
metadata:
|
||||
name: "Cross-Site Scripting (XSS) via Unsafe HTML"
|
||||
description: "Detects unsafe HTML rendering that could lead to XSS vulnerabilities"
|
||||
severity: "HIGH"
|
||||
category: "security"
|
||||
subcategory: "xss"
|
||||
|
||||
owasp:
|
||||
- "A03:2021 - Injection"
|
||||
cwe:
|
||||
- "CWE-79: Cross-site Scripting (XSS)"
|
||||
- "CWE-80: Improper Neutralization of Script-Related HTML Tags"
|
||||
|
||||
compliance:
|
||||
- "PCI-DSS 6.5.7: Cross-site scripting"
|
||||
- "NIST 800-53 SI-10: Information Input Validation"
|
||||
|
||||
confidence: "HIGH"
|
||||
likelihood: "MEDIUM"
|
||||
impact: "HIGH"
|
||||
|
||||
references:
|
||||
- "https://owasp.org/www-community/attacks/xss/"
|
||||
- "https://cwe.mitre.org/data/definitions/79.html"
|
||||
- "https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html"
|
||||
|
||||
languages:
|
||||
- javascript
|
||||
- typescript
|
||||
- jsx
|
||||
- tsx
|
||||
|
||||
pattern-either:
|
||||
- pattern: |
|
||||
dangerouslySetInnerHTML={{__html: $VAR}}
|
||||
- pattern: |
|
||||
innerHTML = $VAR
|
||||
|
||||
message: |
|
||||
Potential XSS vulnerability detected. Setting HTML content directly from
|
||||
user input without sanitization can allow attackers to inject malicious
|
||||
JavaScript code.
|
||||
|
||||
Use one of these safe alternatives:
|
||||
- React: Use {userInput} for automatic escaping
|
||||
- DOMPurify: const clean = DOMPurify.sanitize(dirty);
|
||||
- Framework-specific sanitizers
|
||||
|
||||
See: https://owasp.org/www-community/attacks/xss/
|
||||
|
||||
examples:
|
||||
- vulnerable: |
|
||||
// Vulnerable: Unsanitized HTML
|
||||
function UserComment({ comment }) {
|
||||
return <div dangerouslySetInnerHTML={{__html: comment}} />;
|
||||
}
|
||||
|
||||
- fixed: |
|
||||
// Fixed: Sanitized with DOMPurify
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
function UserComment({ comment }) {
|
||||
const sanitized = DOMPurify.sanitize(comment);
|
||||
return <div dangerouslySetInnerHTML={{__html: sanitized}} />;
|
||||
}
|
||||
|
||||
# Example Rule 4: Insecure Cryptography
|
||||
- id: weak-cryptographic-algorithm
|
||||
metadata:
|
||||
name: "Weak Cryptographic Algorithm"
|
||||
description: "Detects use of weak or deprecated cryptographic algorithms"
|
||||
severity: "HIGH"
|
||||
category: "security"
|
||||
subcategory: "cryptography"
|
||||
|
||||
owasp:
|
||||
- "A02:2021 - Cryptographic Failures"
|
||||
cwe:
|
||||
- "CWE-327: Use of a Broken or Risky Cryptographic Algorithm"
|
||||
- "CWE-326: Inadequate Encryption Strength"
|
||||
|
||||
compliance:
|
||||
- "PCI-DSS 4.1: Use strong cryptography"
|
||||
- "NIST 800-53 SC-13: Cryptographic Protection"
|
||||
- "GDPR Article 32: Security of processing"
|
||||
|
||||
confidence: "HIGH"
|
||||
likelihood: "MEDIUM"
|
||||
impact: "HIGH"
|
||||
|
||||
references:
|
||||
- "https://cwe.mitre.org/data/definitions/327.html"
|
||||
- "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/09-Testing_for_Weak_Cryptography/"
|
||||
|
||||
languages:
|
||||
- python
|
||||
- javascript
|
||||
- java
|
||||
|
||||
pattern-either:
|
||||
- pattern: |
|
||||
hashlib.md5(...)
|
||||
- pattern: |
|
||||
hashlib.sha1(...)
|
||||
- pattern: |
|
||||
crypto.createHash('md5')
|
||||
- pattern: |
|
||||
crypto.createHash('sha1')
|
||||
|
||||
message: |
|
||||
Weak cryptographic algorithm detected (MD5 or SHA1). These algorithms are
|
||||
considered cryptographically broken and should not be used for security purposes.
|
||||
|
||||
Use strong alternatives:
|
||||
- For hashing: SHA-256, SHA-384, or SHA-512
|
||||
- For password hashing: bcrypt, argon2, or PBKDF2
|
||||
- Python: hashlib.sha256()
|
||||
- Node.js: crypto.createHash('sha256')
|
||||
|
||||
See: https://cwe.mitre.org/data/definitions/327.html
|
||||
|
||||
examples:
|
||||
- vulnerable: |
|
||||
# Vulnerable: MD5 hash
|
||||
import hashlib
|
||||
hash_value = hashlib.md5(data).hexdigest()
|
||||
|
||||
- fixed: |
|
||||
# Fixed: SHA-256 hash
|
||||
import hashlib
|
||||
hash_value = hashlib.sha256(data).hexdigest()
|
||||
|
||||
# Rule Configuration
|
||||
configuration:
|
||||
# Global settings
|
||||
enabled: true
|
||||
severity_threshold: "MEDIUM" # Report findings at MEDIUM severity and above
|
||||
|
||||
# Performance tuning
|
||||
max_file_size_kb: 1024
|
||||
exclude_patterns:
|
||||
- "test/*"
|
||||
- "tests/*"
|
||||
- "node_modules/*"
|
||||
- "vendor/*"
|
||||
- "*.min.js"
|
||||
|
||||
# False positive reduction
|
||||
confidence_threshold: "MEDIUM" # Only report findings with MEDIUM confidence or higher
|
||||
|
||||
# Rule Metadata Schema
|
||||
# This section documents the expected structure for rules
|
||||
metadata_schema:
|
||||
required:
|
||||
- id: "Unique identifier for the rule (kebab-case)"
|
||||
- name: "Human-readable rule name"
|
||||
- description: "What the rule detects"
|
||||
- severity: "CRITICAL | HIGH | MEDIUM | LOW | INFO"
|
||||
- category: "security | best-practice | performance"
|
||||
|
||||
optional:
|
||||
- subcategory: "Specific type (injection, xss, secrets, etc.)"
|
||||
- owasp: "OWASP Top 10 mappings"
|
||||
- cwe: "CWE identifier(s)"
|
||||
- mitre_attack: "MITRE ATT&CK technique(s)"
|
||||
- compliance: "Compliance standard references"
|
||||
- confidence: "Detection confidence level"
|
||||
- likelihood: "Likelihood of exploitation"
|
||||
- impact: "Potential impact if exploited"
|
||||
- references: "External documentation links"
|
||||
|
||||
# Usage Instructions:
|
||||
#
|
||||
# 1. Copy this template when creating new security rules
|
||||
# 2. Update metadata fields with appropriate framework mappings
|
||||
# 3. Customize detection patterns for your tool (Semgrep, OPA, etc.)
|
||||
# 4. Provide clear remediation guidance in the message field
|
||||
# 5. Include both vulnerable and fixed code examples
|
||||
# 6. Test rules on real codebases before deployment
|
||||
#
|
||||
# Best Practices:
|
||||
# - Map to multiple frameworks (OWASP, CWE, MITRE ATT&CK)
|
||||
# - Include compliance standard references
|
||||
# - Provide actionable remediation guidance
|
||||
# - Show code examples (vulnerable vs. fixed)
|
||||
# - Tune confidence levels to reduce false positives
|
||||
# - Exclude test directories to reduce noise
|
||||
293
skills/appsec/api-spectral/assets/spectral-owasp.yaml
Normal file
293
skills/appsec/api-spectral/assets/spectral-owasp.yaml
Normal file
@@ -0,0 +1,293 @@
|
||||
# Comprehensive OWASP API Security Top 10 2023 Spectral Ruleset
|
||||
# This ruleset enforces OWASP API Security best practices for OpenAPI specifications
|
||||
|
||||
extends: ["spectral:oas"]
|
||||
|
||||
rules:
|
||||
# ============================================================================
|
||||
# API1:2023 - Broken Object Level Authorization
|
||||
# ============================================================================
|
||||
|
||||
operation-security-defined:
|
||||
description: All operations must have security requirements defined (OWASP API1)
|
||||
severity: error
|
||||
given: $.paths[*][get,post,put,patch,delete,head]
|
||||
then:
|
||||
- field: security
|
||||
function: truthy
|
||||
message: "Operations must define security requirements to prevent unauthorized object access (OWASP API1:2023 - Broken Object Level Authorization)"
|
||||
|
||||
id-parameters-require-security:
|
||||
description: Operations with ID parameters must have security defined
|
||||
severity: error
|
||||
given: $.paths[?(@property =~ /(\/\{id\}|\/\{.*[_-]id\})/i)][get,put,patch,delete]
|
||||
then:
|
||||
- field: security
|
||||
function: truthy
|
||||
message: "Operations with ID parameters require security to prevent IDOR vulnerabilities (OWASP API1:2023)"
|
||||
|
||||
# ============================================================================
|
||||
# API2:2023 - Broken Authentication
|
||||
# ============================================================================
|
||||
|
||||
security-schemes-required:
|
||||
description: API must define security schemes (OWASP API2)
|
||||
severity: error
|
||||
given: $.components
|
||||
then:
|
||||
- field: securitySchemes
|
||||
function: truthy
|
||||
message: "API must define security schemes to prevent authentication bypass (OWASP API2:2023 - Broken Authentication)"
|
||||
|
||||
no-http-basic-auth:
|
||||
description: HTTP Basic authentication is insecure for APIs
|
||||
severity: error
|
||||
given: $.components.securitySchemes[*]
|
||||
then:
|
||||
- field: scheme
|
||||
function: pattern
|
||||
functionOptions:
|
||||
notMatch: "^basic$"
|
||||
message: "HTTP Basic authentication transmits credentials in plain text - use OAuth2, API key, or JWT (OWASP API2:2023)"
|
||||
|
||||
bearer-format-specified:
|
||||
description: Bearer authentication should specify token format (JWT recommended)
|
||||
severity: warn
|
||||
given: $.components.securitySchemes[?(@.type == 'http' && @.scheme == 'bearer')]
|
||||
then:
|
||||
- field: bearerFormat
|
||||
function: truthy
|
||||
message: "Bearer authentication should specify token format (bearerFormat: JWT) for clarity (OWASP API2:2023)"
|
||||
|
||||
# ============================================================================
|
||||
# API3:2023 - Broken Object Property Level Authorization
|
||||
# ============================================================================
|
||||
|
||||
no-additional-properties:
|
||||
description: Prevent mass assignment by disabling additionalProperties
|
||||
severity: warn
|
||||
given: $.components.schemas[?(@.type == 'object')]
|
||||
then:
|
||||
- field: additionalProperties
|
||||
function: falsy
|
||||
message: "Set additionalProperties to false to prevent mass assignment vulnerabilities (OWASP API3:2023 - Broken Object Property Level Authorization)"
|
||||
|
||||
schemas-have-properties:
|
||||
description: Object schemas should explicitly define properties
|
||||
severity: warn
|
||||
given: $.components.schemas[?(@.type == 'object')]
|
||||
then:
|
||||
- field: properties
|
||||
function: truthy
|
||||
message: "Explicitly define object properties to control data exposure (OWASP API3:2023)"
|
||||
|
||||
# ============================================================================
|
||||
# API4:2023 - Unrestricted Resource Consumption
|
||||
# ============================================================================
|
||||
|
||||
rate-limit-headers-documented:
|
||||
description: API should document rate limiting headers
|
||||
severity: warn
|
||||
given: $.paths[*][get,post,put,patch,delete].responses[?(@property < '300')].headers
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
anyOf:
|
||||
- required: [X-RateLimit-Limit]
|
||||
- required: [X-Rate-Limit-Limit]
|
||||
- required: [RateLimit-Limit]
|
||||
message: "Document rate limiting headers to communicate resource consumption limits (OWASP API4:2023 - Unrestricted Resource Consumption)"
|
||||
|
||||
pagination-parameters-present:
|
||||
description: List operations should support pagination
|
||||
severity: warn
|
||||
given: $.paths[*].get
|
||||
then:
|
||||
- field: parameters
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: array
|
||||
contains:
|
||||
anyOf:
|
||||
- properties:
|
||||
name:
|
||||
enum: [limit, per_page, page_size]
|
||||
- properties:
|
||||
name:
|
||||
enum: [offset, page, cursor]
|
||||
message: "List operations should support pagination (limit/offset or cursor) to prevent resource exhaustion (OWASP API4:2023)"
|
||||
|
||||
# ============================================================================
|
||||
# API5:2023 - Broken Function Level Authorization
|
||||
# ============================================================================
|
||||
|
||||
write-operations-require-security:
|
||||
description: Write operations must have security requirements
|
||||
severity: error
|
||||
given: $.paths[*][post,put,patch,delete]
|
||||
then:
|
||||
- field: security
|
||||
function: truthy
|
||||
message: "Write operations must have security requirements to prevent unauthorized function access (OWASP API5:2023 - Broken Function Level Authorization)"
|
||||
|
||||
admin-paths-require-security:
|
||||
description: Admin endpoints must have strict security
|
||||
severity: error
|
||||
given: $.paths[?(@property =~ /admin/i)][*]
|
||||
then:
|
||||
- field: security
|
||||
function: truthy
|
||||
message: "Admin endpoints require security requirements with appropriate scopes (OWASP API5:2023)"
|
||||
|
||||
# ============================================================================
|
||||
# API7:2023 - Server Side Request Forgery
|
||||
# ============================================================================
|
||||
|
||||
no-url-parameters:
|
||||
description: Avoid URL parameters to prevent SSRF attacks
|
||||
severity: warn
|
||||
given: $.paths[*][*].parameters[?(@.in == 'query' || @.in == 'body')][?(@.name =~ /(url|uri|link|callback|redirect|webhook)/i)]
|
||||
then:
|
||||
function: truthy
|
||||
message: "URL parameters can enable SSRF attacks - validate and whitelist destination URLs (OWASP API7:2023 - Server Side Request Forgery)"
|
||||
|
||||
# ============================================================================
|
||||
# API8:2023 - Security Misconfiguration
|
||||
# ============================================================================
|
||||
|
||||
servers-use-https:
|
||||
description: All API servers must use HTTPS
|
||||
severity: error
|
||||
given: $.servers[*].url
|
||||
then:
|
||||
function: pattern
|
||||
functionOptions:
|
||||
match: "^https://"
|
||||
message: "Server URLs must use HTTPS protocol for secure communication (OWASP API8:2023 - Security Misconfiguration)"
|
||||
|
||||
no-example-servers:
|
||||
description: Replace example server URLs with actual endpoints
|
||||
severity: error
|
||||
given: $.servers[*].url
|
||||
then:
|
||||
function: pattern
|
||||
functionOptions:
|
||||
notMatch: "example\\.com"
|
||||
message: "Replace example.com with actual production server URLs (OWASP API8:2023)"
|
||||
|
||||
security-headers-in-responses:
|
||||
description: Document security headers in responses
|
||||
severity: info
|
||||
given: $.paths[*][*].responses[*].headers
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
anyOf:
|
||||
- required: [X-Content-Type-Options]
|
||||
- required: [X-Frame-Options]
|
||||
- required: [Strict-Transport-Security]
|
||||
- required: [Content-Security-Policy]
|
||||
message: "Consider documenting security headers (X-Content-Type-Options, X-Frame-Options, HSTS, CSP) (OWASP API8:2023)"
|
||||
|
||||
# ============================================================================
|
||||
# API9:2023 - Improper Inventory Management
|
||||
# ============================================================================
|
||||
|
||||
api-version-required:
|
||||
description: API specification must include version
|
||||
severity: error
|
||||
given: $.info
|
||||
then:
|
||||
- field: version
|
||||
function: truthy
|
||||
message: "API version must be specified for proper inventory management (OWASP API9:2023 - Improper Inventory Management)"
|
||||
|
||||
semantic-versioning-format:
|
||||
description: Use semantic versioning for API versions
|
||||
severity: warn
|
||||
given: $.info.version
|
||||
then:
|
||||
function: pattern
|
||||
functionOptions:
|
||||
match: "^\\d+\\.\\d+(\\.\\d+)?$"
|
||||
message: "Use semantic versioning format (MAJOR.MINOR.PATCH) for API versions (OWASP API9:2023)"
|
||||
|
||||
contact-info-required:
|
||||
description: API must include contact information
|
||||
severity: warn
|
||||
given: $.info
|
||||
then:
|
||||
- field: contact
|
||||
function: truthy
|
||||
message: "Include contact information for API support and security reporting (OWASP API9:2023)"
|
||||
|
||||
deprecated-endpoints-documented:
|
||||
description: Deprecated endpoints must document migration path
|
||||
severity: warn
|
||||
given: $.paths[*][*][?(@.deprecated == true)]
|
||||
then:
|
||||
- field: description
|
||||
function: pattern
|
||||
functionOptions:
|
||||
match: "(deprecate|migrate|alternative|replacement|use instead)"
|
||||
message: "Deprecated endpoints must document migration path and timeline (OWASP API9:2023)"
|
||||
|
||||
# ============================================================================
|
||||
# API10:2023 - Unsafe Consumption of APIs
|
||||
# ============================================================================
|
||||
|
||||
validate-external-api-responses:
|
||||
description: Document validation of external API responses
|
||||
severity: info
|
||||
given: $.paths[*][*].responses[*].content[*].schema
|
||||
then:
|
||||
- field: description
|
||||
function: truthy
|
||||
message: "Document schema validation for all API responses, especially from external APIs (OWASP API10:2023 - Unsafe Consumption of APIs)"
|
||||
|
||||
# ============================================================================
|
||||
# Additional Security Best Practices
|
||||
# ============================================================================
|
||||
|
||||
no-pii-in-query-parameters:
|
||||
description: Prevent PII exposure in URL query parameters
|
||||
severity: error
|
||||
given: $.paths[*][*].parameters[?(@.in == 'query')].name
|
||||
then:
|
||||
function: pattern
|
||||
functionOptions:
|
||||
notMatch: "(?i)(ssn|social.?security|credit.?card|password|secret|token|api.?key|private|passport|driver.?license)"
|
||||
message: "Query parameters must not contain PII or sensitive data - use request body with HTTPS instead"
|
||||
|
||||
consistent-error-response-format:
|
||||
description: Error responses should follow consistent format
|
||||
severity: warn
|
||||
given: $.paths[*][*].responses[?(@property >= '400')].content.application/json.schema
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
required: [error, message]
|
||||
message: "Error responses should follow consistent format with 'error' and 'message' fields"
|
||||
|
||||
no-verbose-error-details:
|
||||
description: 5xx errors should not expose internal details
|
||||
severity: warn
|
||||
given: $.paths[*][*].responses[?(@property >= '500')].content[*].schema.properties
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
not:
|
||||
anyOf:
|
||||
- required: [stack_trace]
|
||||
- required: [stackTrace]
|
||||
- required: [debug_info]
|
||||
message: "5xx error responses should not expose stack traces or internal details in production"
|
||||
Reference in New Issue
Block a user