Skip to content

feat: Implemented soroban Analysis Artifact with full synchrony #286

feat: Implemented soroban Analysis Artifact with full synchrony

feat: Implemented soroban Analysis Artifact with full synchrony #286

Workflow file for this run

name: GasGuard Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
gasguard-scan:
name: GasGuard Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 10.10.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: |
if [ -f "pnpm-lock.yaml" ]; then
pnpm install --frozen-lockfile
else
echo "No pnpm-lock.yaml found"
exit 1
fi
- name: Build GasGuard
run: |
echo "Building GasGuard..."
pnpm run build || echo "Build completed with warnings"
- name: Find Solidity files
id: find-files
run: |
files=$(find . -path "*/node_modules" -prune -o -name "*.sol" -type f -print | tr '\n' ' ')
echo "files=$files" >> $GITHUB_OUTPUT
echo "Found files: $files"
- name: Run GasGuard Scan
id: gasguard-scan
run: |
echo "Running GasGuard scan..."
# Create scan results directory
mkdir -p gasguard-results
# Initialize scan variables
total_issues=0
high_severity_issues=0
critical_issues=0
total_gas_savings=0
# Scan each Solidity file
IFS=' ' read -ra FILES <<< "${{ steps.find-files.outputs.files }}"
for file in "${FILES[@]}"; do
if [ -f "$file" ]; then
echo "Scanning: $file"
# Run GasGuard analysis (simulated - in real implementation would use actual CLI)
scan_result=$(node -e "
const fs = require('fs');
const content = fs.readFileSync('$file', 'utf8');
// Simulate GasGuard rule checks
const issues = [];
let gasSavings = 0;
// Check for unused variables (SOL-003)
const lines = content.split('\n');
lines.forEach((line, index) => {
if (line.includes('uint ') && line.includes(';') && !line.includes('//')) {
const varMatch = line.match(/uint\s+(\w+)/);
if (varMatch) {
const varName = varMatch[1];
const usageCount = (content.match(new RegExp(varName, 'g')) || []).length;
if (usageCount <= 1) {
issues.push({
rule: 'SOL-003',
severity: 'Warning',
message: \`Variable '\${varName}' is declared but never used\`,
line: index + 1,
gasSavings: 100
});
gasSavings += 100;
}
}
}
// Check for string storage (SOL-001)
if (line.includes('string ') && line.includes('public')) {
issues.push({
rule: 'SOL-001',
severity: 'Warning',
message: 'Consider replacing string with bytes32 for short values',
line: index + 1,
gasSavings: 5000
});
gasSavings += 5000;
}
});
console.log(JSON.stringify({
file: '$file',
issues: issues,
gasSavings: gasSavings
}));
")
# Parse scan result
issues_count=$(echo "$scan_result" | jq -r '.issues | length')
high_count=$(echo "$scan_result" | jq -r '.issues[] | select(.severity == "Error" or .severity == "Critical") | .rule' | wc -l)
critical_count=$(echo "$scan_result" | jq -r '.issues[] | select(.severity == "Critical") | .rule' | wc -l)
file_gas_savings=$(echo "$scan_result" | jq -r '.gasSavings // 0')
# Update totals
total_issues=$((total_issues + issues_count))
high_severity_issues=$((high_severity_issues + high_count))
critical_issues=$((critical_issues + critical_count))
total_gas_savings=$((total_gas_savings + file_gas_savings))
# Save individual file results
echo "$scan_result" > "gasguard-results/$(basename $file .sol).json"
echo " - Issues: $issues_count, High Severity: $high_count, Critical: $critical_count, Gas Savings: $file_gas_savings"
fi
done
# Create summary report
cat > gasguard-results/summary.json << EOF
{
"total_files_scanned": $(echo "$FILES" | wc -w),
"total_issues": $total_issues,
"high_severity_issues": $high_severity_issues,
"critical_issues": $critical_issues,
"total_gas_savings": $total_gas_savings,
"scan_timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF
echo "Scan completed:"
echo " - Files scanned: $(echo "$FILES" | wc -w)"
echo " - Total issues: $total_issues"
echo " - High severity issues: $high_severity_issues"
echo " - Critical issues: $critical_issues"
echo " - Total potential gas savings: $total_gas_savings"
# Set outputs for GitHub Actions
echo "total-issues=$total_issues" >> $GITHUB_OUTPUT
echo "high-severity=$high_severity_issues" >> $GITHUB_OUTPUT
echo "critical=$critical_issues" >> $GITHUB_OUTPUT
echo "gas-savings=$total_gas_savings" >> $GITHUB_OUTPUT
- name: Generate Scan Report
run: |
echo "# GasGuard Scan Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Files Scanned**: $(jq -r '.total_files_scanned' gasguard-results/summary.json)" >> $GITHUB_STEP_SUMMARY
echo "- **Total Issues**: ${{ steps.gasguard-scan.outputs.total-issues }}" >> $GITHUB_STEP_SUMMARY
echo "- **High Severity Issues**: ${{ steps.gasguard-scan.outputs.high-severity }}" >> $GITHUB_STEP_SUMMARY
echo "- **Critical Issues**: ${{ steps.gasguard-scan.outputs.critical }}" >> $GITHUB_STEP_SUMMARY
echo "- **Potential Gas Savings**: ${{ steps.gasguard-scan.outputs.gas-savings }} gas" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Add detailed findings
echo "## Detailed Findings" >> $GITHUB_STEP_SUMMARY
for result_file in gasguard-results/*.json; do
if [ "$result_file" != "gasguard-results/summary.json" ]; then
filename=$(basename "$result_file" .json)
issues_count=$(jq -r '.issues | length' "$result_file")
if [ "$issues_count" -gt 0 ]; then
echo "### $filename.sol ($issues_count issues)" >> $GITHUB_STEP_SUMMARY
jq -r '.issues[] | "- Line \(.line): \(.message) (\(.rule))"' "$result_file" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
fi
done
- name: Check for High Severity Issues
id: severity-check
run: |
if [ "${{ steps.gasguard-scan.outputs.critical }}" -gt 0 ]; then
echo "status=error" >> $GITHUB_OUTPUT
echo "message=Critical security issues detected" >> $GITHUB_OUTPUT
elif [ "${{ steps.gasguard-scan.outputs.high-severity }}" -gt 5 ]; then
echo "status=error" >> $GITHUB_OUTPUT
echo "message=Too many high severity issues detected" >> $GITHUB_OUTPUT
elif [ "${{ steps.gasguard-scan.outputs.high-severity }}" -gt 0 ]; then
echo "status=warning" >> $GITHUB_OUTPUT
echo "message=High severity issues detected" >> $GITHUB_OUTPUT
else
echo "status=success" >> $GITHUB_OUTPUT
echo "message=No critical issues detected" >> $GITHUB_OUTPUT
fi
- name: Create Status Badge
run: |
status="${{ steps.severity-check.outputs.status }}"
message="${{ steps.severity-check.outputs.message }}"
case $status in
"error")
color="red"
;;
"warning")
color="yellow"
;;
*)
color="green"
;;
esac
echo "![GasGuard Scan](https://img.shields.io/badge/GasGuard-$message-$color)" >> $GITHUB_STEP_SUMMARY
- name: Upload Scan Results
uses: actions/upload-artifact@v4
if: always()
with:
name: gasguard-scan-results
path: gasguard-results/
retention-days: 30
- name: Fail on Critical Issues
if: steps.severity-check.outputs.status == 'error'
run: |
echo "::error::${{ steps.severity-check.outputs.message }}"
echo "::error::Please fix the critical issues before merging."
exit 1
- name: Warn on High Severity Issues
if: steps.severity-check.outputs.status == 'warning'
run: |
echo "::warning::${{ steps.severity-check.outputs.message }}"
echo "::warning::Consider addressing high severity issues before merging."
- name: Success Message
if: steps.severity-check.outputs.status == 'success'
run: |
echo "✅ GasGuard scan passed successfully!"
echo "🎉 Potential gas savings: ${{ steps.gasguard-scan.outputs.gas-savings }} gas"
gasguard-comment:
name: GasGuard PR Comment
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
needs: gasguard-scan
continue-on-error: true
steps:
- name: Download Scan Results
uses: actions/download-artifact@v4
with:
name: gasguard-scan-results
path: scan-results/
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
try {
const summary = JSON.parse(fs.readFileSync('scan-results/summary.json', 'utf8'));
let status = '✅ PASSED';
let statusMessage = '🎉 **No critical issues found!**';
if (summary.critical_issues > 0) {
status = '❌ FAILED';
statusMessage = '🚨 **Critical issues detected! Please fix before merging.**';
} else if (summary.high_severity_issues > 0) {
status = '⚠️ WARNING';
statusMessage = '⚠️ **High severity issues detected. Consider addressing before merging.**';
}
const comment = "## 🔍 GasGuard Scan Results\n\n" +
"**Scan Summary:**\n" +
"- 📁 Files scanned: " + summary.total_files_scanned + "\n" +
"- ⚠️ Total issues: " + summary.total_issues + "\n" +
"- 🚨 High severity issues: " + summary.high_severity_issues + "\n" +
"- 💀 Critical issues: " + summary.critical_issues + "\n" +
"- ⛽ Potential gas savings: " + summary.total_gas_savings + " gas\n\n" +
"**Status: " + status + "**\n\n" +
statusMessage + "\n\n" +
"*Scan completed on " + new Date(summary.scan_timestamp).toLocaleString() + "*";
try {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
} catch (commentError) {
console.log('Could not post PR comment (may lack write permissions on fork PRs):', commentError.message);
}
} catch (error) {
console.log('Could not read scan results:', error.message);
}