From e917b02467791ff5eb147b080029fa7434bdddcd Mon Sep 17 00:00:00 2001 From: Yonas Tewabe Date: Fri, 12 Jun 2026 13:00:43 +0300 Subject: [PATCH] Delete .github/workflows/malware-scan.yml --- .github/workflows/malware-scan.yml | 241 ----------------------------- 1 file changed, 241 deletions(-) delete mode 100644 .github/workflows/malware-scan.yml diff --git a/.github/workflows/malware-scan.yml b/.github/workflows/malware-scan.yml deleted file mode 100644 index 8e05957ae..000000000 --- a/.github/workflows/malware-scan.yml +++ /dev/null @@ -1,241 +0,0 @@ -name: Malware & Obfuscation Scan - -on: - push: - branches: ["**"] - pull_request: - branches: ["**"] - # Allow manual triggering for ad-hoc full scans - workflow_dispatch: - inputs: - scan_path: - description: "Sub-directory to scan (leave blank for full repo)" - required: false - default: "." - -# Prevent concurrent scans on the same ref from stepping on each other -concurrency: - group: malware-scan-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - # Needed if you later add GitHub Code Scanning / SARIF upload - security-events: write - -jobs: - malware-scan: - name: Scan for malicious / obfuscated code - runs-on: self-hosted - timeout-minutes: 15 - - steps: - # ── 1. Checkout ──────────────────────────────────────────────────────── - - name: Checkout repository - uses: actions/checkout@v4 - with: - # Full history lets the scanner see every file, not just the diff. - # For very large repos you can set fetch-depth: 1 to speed things up, - # but you may miss injected files in unchanged paths. - fetch-depth: 0 - - # ── 2. Setup Node ────────────────────────────────────────────────────── - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - - # ── 3. Install scanner ───────────────────────────────────────────────── - # The scanner is pure Node.js stdlib — no npm install needed. - # We just copy the script into a known location inside the runner. - - name: Install scanner script - run: | - mkdir -p "$RUNNER_TOOL_CACHE/malware-scanner" - cp .github/scripts/scan-malware.js \ - "$RUNNER_TOOL_CACHE/malware-scanner/scan-malware.js" - chmod +x "$RUNNER_TOOL_CACHE/malware-scanner/scan-malware.js" - - # ── 4. Run the scanner ───────────────────────────────────────────────── - - name: Run malware scanner - id: scan - env: - SCAN_JSON_OUT: ${{ runner.temp }}/scan-results.json - run: | - SCAN_PATH="${{ github.event.inputs.scan_path || '.' }}" - echo "Scanning path: $SCAN_PATH" - echo "────────────────────────────────────────" - - node "$RUNNER_TOOL_CACHE/malware-scanner/scan-malware.js" "$SCAN_PATH" - # The script exits 0 (clean), 1 (threats found), or 2 (internal error). - # We want the step to "succeed" so the upload-artifact step always runs, - # but we'll fail the job in the gate step below. - continue-on-error: true - - # ── 5. Upload JSON report as artifact (always, even on failure) ──────── - # Retained so the full per-file, per-rule detail is always downloadable. - # The Telegram message below links directly to the Actions run where - # this artifact appears. - - name: Upload scan report artifact - if: always() - uses: actions/upload-artifact@v4 - with: - name: malware-scan-report-${{ github.sha }} - path: ${{ runner.temp }}/scan-results.json - retention-days: 90 - if-no-files-found: ignore - - # ── 6. Send Telegram notification (always, even on failure) ────────────── - # Requires two repository secrets: - # TELEGRAM_BOT_TOKEN — from @BotFather (format: 123456:ABC-xxx) - # TELEGRAM_CHAT_ID — target chat/channel ID (format: -100xxxxxxxxxx) - # - # Intentionally short — plain HTML mode, no code spans, no snippets. - # All special characters that would break MarkdownV2 are avoided entirely. - # Full details are in the artifact linked via the Actions run URL. - - name: Send Telegram notification - if: always() - env: - TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} - TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} - SCAN_JSON: ${{ runner.temp }}/scan-results.json - GH_REPO: ${{ github.repository }} - GH_SHA: ${{ github.sha }} - GH_REF: ${{ github.ref_name }} - GH_ACTOR: ${{ github.actor }} - GH_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - run: | - node - << 'EOF' - const fs = require('fs'); - const https = require('https'); - - const token = process.env.TELEGRAM_BOT_TOKEN; - const chatId = process.env.TELEGRAM_CHAT_ID; - const repo = process.env.GH_REPO; - const sha = process.env.GH_SHA.slice(0, 7); - const ref = process.env.GH_REF; - const actor = process.env.GH_ACTOR; - const runUrl = process.env.GH_RUN_URL; - - // HTML-escape only the four characters HTML cares about. - // Using HTML parse_mode means code snippets, file paths, and rule IDs - // with special characters can never break the parser. - const h = s => String(s) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); - - let findings = []; - try { - findings = JSON.parse(fs.readFileSync(process.env.SCAN_JSON, 'utf8')); - } catch { /* missing file = clean run or scanner error */ } - - const counts = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 }; - for (const f of findings) counts[f.severity] = (counts[f.severity] || 0) + 1; - - const isClean = findings.length === 0; - - // ── Unique affected files ────────────────────────────────────────── - const affectedFiles = [...new Set(findings.map(f => f.file))]; - - // ── Build a short, fixed-size message ───────────────────────────── - // No snippets, no descriptions — just counts, affected files, and a - // direct link to the artifact. Stays well under 500 chars. - let lines = []; - - if (isClean) { - lines.push('✅ Malware Scan — Clean'); - } else { - lines.push('🚨 Malware Scan — THREATS DETECTED'); - } - - lines.push(''); - lines.push(`Repo: ${h(repo)}`); - lines.push(`Branch: ${h(ref)} Commit: ${h(sha)}`); - lines.push(`Actor: ${h(actor)}`); - - if (!isClean) { - lines.push(''); - lines.push( - `Findings: ` + - `🔴 ${counts.CRITICAL} CRITICAL ` + - `🟠 ${counts.HIGH} HIGH ` + - `🟡 ${counts.MEDIUM} MEDIUM ` + - `⚪ ${counts.LOW} LOW` - ); - lines.push(''); - lines.push(`Affected files (${affectedFiles.length}):`); - // Cap at 10 files to keep the message short - const shown = affectedFiles.slice(0, 10); - for (const f of shown) lines.push(` • ${h(f)}`); - if (affectedFiles.length > 10) { - lines.push(` • … and ${affectedFiles.length - 10} more`); - } - } - - lines.push(''); - lines.push(`📋 View full run & download report artifact`); - - const text = lines.join('\n'); - - // ── Send via Bot API (HTML parse mode) ──────────────────────────── - const body = JSON.stringify({ - chat_id: chatId, - text, - parse_mode: 'HTML', - disable_web_page_preview: true, - }); - - const options = { - hostname: 'api.telegram.org', - path: `/bot${token}/sendMessage`, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(body), - }, - }; - - const req = https.request(options, res => { - let data = ''; - res.on('data', chunk => data += chunk); - res.on('end', () => { - const parsed = JSON.parse(data); - if (!parsed.ok) { - console.error('Telegram API error:', JSON.stringify(parsed)); - process.exit(1); - } - console.log('Telegram notification sent successfully.'); - }); - }); - req.on('error', err => { - console.error('Request failed:', err.message); - process.exit(1); - }); - req.write(body); - req.end(); - EOF - - # ── 7. Gate — fail the workflow if threats were found ────────────────── - # Runs after the Telegram step so the alert always fires first. - - name: Fail workflow if threats detected - if: steps.scan.outcome == 'failure' - run: | - echo "::error::⛔ Malicious or highly-suspicious code patterns were detected." - echo "::error::Check your Telegram channel for the summary." - echo "::error::Download the 'malware-scan-report' artifact for full details." - echo "::error::Do NOT merge or deploy this branch until findings are reviewed." - exit 1 - - # ── 7. (Optional) Diff-only scan on PRs for faster feedback ─────────── - # Uncomment this block if you want a second, faster pass that only - # checks the files changed in the PR diff. - # - # - name: Diff-only scan (PR only) - # if: github.event_name == 'pull_request' - # env: - # SCAN_JSON_OUT: ${{ runner.temp }}/scan-results-diff.json - # run: | - # git diff --name-only origin/${{ github.base_ref }}...HEAD \ - # | grep -E '\.(js|cjs|mjs|ts|tsx|jsx)$' \ - # | xargs -I{} node "$RUNNER_TOOL_CACHE/malware-scanner/scan-malware.js" {}