mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 23:53:25 +00:00
git-subtree-dir: packages/browseros-agent git-subtree-mainline:8f148d0918git-subtree-split:90bd4be300
190 lines
6.1 KiB
YAML
190 lines
6.1 KiB
YAML
name: Daily Security Audit
|
|
|
|
on:
|
|
schedule:
|
|
# Runs at midnight IST (6:30 PM UTC previous day)
|
|
- cron: "30 18 * * *"
|
|
workflow_dispatch: # Allows manual triggering
|
|
|
|
jobs:
|
|
security-audit:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup Bun
|
|
uses: oven-sh/setup-bun@v2
|
|
|
|
- name: Install dependencies
|
|
run: bun ci
|
|
|
|
- name: Run security audit
|
|
id: audit
|
|
continue-on-error: true
|
|
run: |
|
|
# Run audit and capture output (skip the version line)
|
|
bun audit --json 2>&1 | tail -n 1 > audit-results.json || true
|
|
|
|
# Check if vulnerabilities exist
|
|
VULN_COUNT=$(cat audit-results.json | bun -e "const data = JSON.parse(require('fs').readFileSync(0, 'utf-8')); console.log(Object.keys(data).reduce((sum, pkg) => sum + data[pkg].length, 0))")
|
|
echo "vuln_count=$VULN_COUNT" >> $GITHUB_OUTPUT
|
|
|
|
- name: Parse audit results
|
|
id: parse
|
|
if: always()
|
|
run: |
|
|
cat > parse-audit.ts << 'EOF'
|
|
const fs = require('fs');
|
|
const auditData = JSON.parse(fs.readFileSync('audit-results.json', 'utf-8'));
|
|
|
|
// Collect all vulnerabilities from all packages
|
|
const allVulns: any[] = [];
|
|
let totalCount = 0;
|
|
|
|
for (const [packageName, vulns] of Object.entries(auditData)) {
|
|
if (Array.isArray(vulns)) {
|
|
vulns.forEach((vuln: any) => {
|
|
allVulns.push({ ...vuln, packageName });
|
|
totalCount++;
|
|
});
|
|
}
|
|
}
|
|
|
|
if (totalCount === 0) {
|
|
console.log(JSON.stringify({
|
|
text: "✅ *Daily Security Audit - No Vulnerabilities Found*",
|
|
blocks: [
|
|
{
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: "✅ *Daily Security Audit*\n\nNo vulnerabilities found in dependencies!"
|
|
}
|
|
},
|
|
{
|
|
type: "context",
|
|
elements: [
|
|
{
|
|
type: "mrkdwn",
|
|
text: `Repository: ${process.env.GITHUB_REPOSITORY} | Branch: ${process.env.GITHUB_REF_NAME}`
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}));
|
|
process.exit(0);
|
|
}
|
|
|
|
// Count by severity
|
|
const severityCounts = {
|
|
critical: 0,
|
|
high: 0,
|
|
moderate: 0,
|
|
low: 0
|
|
};
|
|
|
|
allVulns.forEach(vuln => {
|
|
severityCounts[vuln.severity as keyof typeof severityCounts]++;
|
|
});
|
|
|
|
let message = `⚠️ *Daily Security Audit - ${totalCount} Vulnerabilit${totalCount === 1 ? 'y' : 'ies'} Found*\n\n`;
|
|
message += `*Severity Breakdown:*\n`;
|
|
message += `• Critical: ${severityCounts.critical}\n`;
|
|
message += `• High: ${severityCounts.high}\n`;
|
|
message += `• Moderate: ${severityCounts.moderate}\n`;
|
|
message += `• Low: ${severityCounts.low}\n\n`;
|
|
|
|
message += `*Top Vulnerabilities:*\n`;
|
|
|
|
// Sort by severity
|
|
const severityOrder = { critical: 0, high: 1, moderate: 2, low: 3 };
|
|
allVulns.sort((a, b) =>
|
|
severityOrder[a.severity as keyof typeof severityOrder] -
|
|
severityOrder[b.severity as keyof typeof severityOrder]
|
|
);
|
|
|
|
allVulns.slice(0, 5).forEach(vuln => {
|
|
const emoji = {
|
|
critical: '🔴',
|
|
high: '🟠',
|
|
moderate: '🟡',
|
|
low: '🟢'
|
|
}[vuln.severity] || '⚪';
|
|
|
|
message += `\n${emoji} *${vuln.title}*\n`;
|
|
message += ` Package: \`${vuln.packageName}\`\n`;
|
|
message += ` Severity: ${vuln.severity.toUpperCase()}\n`;
|
|
message += ` Vulnerable: ${vuln.vulnerable_versions}\n`;
|
|
if (vuln.cwe?.length) {
|
|
message += ` CWE: ${vuln.cwe.join(', ')}\n`;
|
|
}
|
|
if (vuln.cvss?.score) {
|
|
message += ` CVSS: ${vuln.cvss.score}\n`;
|
|
}
|
|
if (vuln.url) {
|
|
message += ` <${vuln.url}|View Details>\n`;
|
|
}
|
|
});
|
|
|
|
if (allVulns.length > 5) {
|
|
message += `\n_...and ${allVulns.length - 5} more vulnerabilit${allVulns.length - 5 === 1 ? 'y' : 'ies'}_`;
|
|
}
|
|
|
|
const payload = {
|
|
text: `⚠️ Security Audit: ${totalCount} vulnerabilit${totalCount === 1 ? 'y' : 'ies'} found`,
|
|
blocks: [
|
|
{
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: message
|
|
}
|
|
},
|
|
{
|
|
type: "actions",
|
|
elements: [
|
|
{
|
|
type: "button",
|
|
text: {
|
|
type: "plain_text",
|
|
text: "View Full Report"
|
|
},
|
|
url: `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
|
|
}
|
|
]
|
|
},
|
|
{
|
|
type: "context",
|
|
elements: [
|
|
{
|
|
type: "mrkdwn",
|
|
text: `Repository: ${process.env.GITHUB_REPOSITORY} | Branch: ${process.env.GITHUB_REF_NAME}`
|
|
}
|
|
]
|
|
}
|
|
]
|
|
};
|
|
|
|
console.log(JSON.stringify(payload));
|
|
EOF
|
|
|
|
bun run parse-audit.ts > slack-payload.json
|
|
|
|
- name: Send to Slack
|
|
if: always()
|
|
env:
|
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
run: |
|
|
curl -X POST \
|
|
-H 'Content-Type: application/json' \
|
|
-d @slack-payload.json \
|
|
$SLACK_WEBHOOK_URL
|
|
|
|
- name: Fail if vulnerabilities found
|
|
if: steps.audit.outputs.vuln_count != '0'
|
|
run: |
|
|
echo "Security audit found vulnerabilities"
|
|
exit 1
|