name: PolinRider Malware Scan # ── Triggers ────────────────────────────────────────────────────────────────── # Runs on every push and every PR targeting main/master/develop. # Also available as a manual trigger (workflow_dispatch) and on a nightly # schedule so dormant infections in older branches are caught too. on: push: branches: ["**"] pull_request: branches: ["**"] schedule: # Nightly full-repo scan at 02:00 UTC - cron: "0 2 * * *" workflow_dispatch: # ── Permissions ─────────────────────────────────────────────────────────────── permissions: contents: read # checkout security-events: write # upload SARIF to GitHub Security tab actions: read checks: write # annotate PRs with scan findings # ── Deployment gate ─────────────────────────────────────────────────────────── # All other jobs (build, test, deploy) should list this job under `needs:`. # If this job fails (exit code 1 from the scanner), the whole workflow stops. jobs: polinrider-scan: name: "PolinRider / Famous Chollima Scan" runs-on: ubuntu-latest # Prevent CI from being disabled by any workflow override if: always() steps: # ── 1. Checkout full history ───────────────────────────────────────────── # Full depth so we can inspect recent commits for temp_auto_push.bat traces - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 # ── 2. Detect suspicious force-push patterns in git history ────────────── - name: Check git history for force-push and timestamp manipulation id: git-check shell: bash run: | echo "=== Checking for suspicious git history patterns ===" # Check for .gitignore entries hiding known malware artifacts GITIGNORE_HITS=0 if [ -f .gitignore ]; then for pattern in "branch_structure.json" "temp_auto_push.bat" "temp_interactive_push.bat"; do if grep -qF "$pattern" .gitignore 2>/dev/null; then echo "::warning file=.gitignore::SUSPICIOUS: .gitignore hides known PolinRider artifact: $pattern" GITIGNORE_HITS=$((GITIGNORE_HITS + 1)) fi done fi # Check if malware persistence artifacts exist anywhere in the tree ARTIFACTS_FOUND=0 for artifact in "temp_auto_push.bat" "temp_interactive_push.bat" "branch_structure.json"; do FOUND=$(find . -name "$artifact" -not -path "./.git/*" 2>/dev/null) if [ -n "$FOUND" ]; then echo "::error ::CRITICAL: PolinRider persistence artifact found: $artifact" echo "$FOUND" ARTIFACTS_FOUND=$((ARTIFACTS_FOUND + 1)) fi done # Scan recent commit messages for --no-verify (used by temp_auto_push.bat) NO_VERIFY_COMMITS=$(git log --oneline -50 --format="%H %s" 2>/dev/null | grep -i "no.verify\|force.*push\|amend" || true) if [ -n "$NO_VERIFY_COMMITS" ]; then echo "::warning ::Recent commits with suspicious metadata (--no-verify / force amend patterns):" echo "$NO_VERIFY_COMMITS" fi # Check for .woff2 files with unusually large sizes (>50KB is suspicious) find . -name "*.woff2" -not -path "./.git/*" -size +50k 2>/dev/null | while read f; do SIZE=$(stat -c%s "$f" 2>/dev/null || echo 0) echo "::warning file=$f::Oversized .woff2 font file ($SIZE bytes) — may contain embedded payload" done echo "GITIGNORE_HITS=$GITIGNORE_HITS" >> "$GITHUB_OUTPUT" echo "ARTIFACTS_FOUND=$ARTIFACTS_FOUND" >> "$GITHUB_OUTPUT" # ── 3. Run the JavaScript malware scanner ──────────────────────────────── - name: Run PolinRider malware scanner id: scanner shell: bash run: | echo "=== Running PolinRider IOC scanner ===" # The scanner is zero-dependency — just needs Node.js (always present on ubuntu-latest) node .github/scripts/scan.js \ --json \ --output scan-report.json \ . SCANNER_EXIT=$? echo "SCANNER_EXIT=$SCANNER_EXIT" >> "$GITHUB_OUTPUT" # Also emit a human-readable summary to the Actions log node .github/scripts/scan.js . || true exit $SCANNER_EXIT # ── 4. Upload scan report as artifact ──────────────────────────────────── # - name: Upload scan report # if: always() # uses: actions/upload-artifact@v4 # with: # name: polinrider-scan-report # path: scan-report.json # retention-days: 90 # # ── 5. Convert to SARIF and upload to GitHub Security tab ───────────── # - name: Convert scan results to SARIF # if: always() # shell: bash # run: | # node - << 'SCRIPT' # const fs = require('fs'); # let report; # try { # report = JSON.parse(fs.readFileSync('scan-report.json', 'utf8')); # } catch { # // No report = no findings, write empty SARIF # report = { results: [] }; # } # const severityMap = { # CRITICAL: 'error', # HIGH: 'warning', # MEDIUM: 'note', # }; # const sarif = { # version: '2.1.0', # $schema: 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json', # runs: [{ # tool: { # driver: { # name: 'PolinRider Malware Scanner', # version: '1.0.0', # informationUri: 'https://github.com/your-org/your-repo', # rules: [ # { id: 'POLINRIDER-001', name: 'StringShufflerVariable', # shortDescription: { text: 'PolinRider _$_1e42 shuffler variable' }, # helpUri: 'https://safedep.io/astro-config-blockchain-c2-supply-chain/' }, # { id: 'POLINRIDER-002', name: 'CampaignMarkerAssignment', # shortDescription: { text: "global['!'] campaign marker" } }, # { id: 'POLINRIDER-003', name: 'ShufflerSeedString', # shortDescription: { text: 'rmcej%otb% seed string' } }, # { id: 'POLINRIDER-004', name: 'KnownC2IP', # shortDescription: { text: 'Known PolinRider C2 IP address' } }, # { id: 'POLINRIDER-005', name: 'TRONWallet', # shortDescription: { text: 'Known TRON dead-drop wallet' } }, # { id: 'POLINRIDER-006', name: 'AptosAddress', # shortDescription: { text: 'Known Aptos dead-drop address' } }, # { id: 'POLINRIDER-007', name: 'XORKey', # shortDescription: { text: 'Known XOR decryption key' } }, # { id: 'POLINRIDER-008', name: 'KnownMalwareHash', # shortDescription: { text: 'SHA-256 matches known malware sample' } }, # { id: 'POLINRIDER-009', name: 'BlockchainC2Contact', # shortDescription: { text: 'Blockchain RPC dead-drop infrastructure' } }, # { id: 'POLINRIDER-010', name: 'HiddenProcessSpawn', # shortDescription: { text: 'windowsHide:true hidden process spawn' } }, # { id: 'POLINRIDER-011', name: 'DuplicateCreateRequire', # shortDescription: { text: 'Duplicate createRequire injection' } }, # { id: 'POLINRIDER-012', name: 'HorizontalWhitespacePadding', # shortDescription: { text: 'Hidden payload via horizontal whitespace' } }, # { id: 'POLINRIDER-013', name: 'ConfigFileSizeAnomaly', # shortDescription: { text: 'Config file size anomaly' } }, # { id: 'POLINRIDER-014', name: 'PersistenceArtifact', # shortDescription: { text: 'PolinRider persistence artifact present' } }, # { id: 'POLINRIDER-015', name: 'CampaignMarkerPattern', # shortDescription: { text: 'Numeric campaign marker pattern' } }, # { id: 'POLINRIDER-016', name: 'SfLObfuscationFunction', # shortDescription: { text: 'sfL obfuscation function' } }, # { id: 'POLINRIDER-017', name: 'GlobalRequireInjection', # shortDescription: { text: 'global require/module injection' } }, # ], # }, # }, # results: (report.results || []).flatMap(file => # (file.findings || []).map(finding => ({ # ruleId: finding.id, # level: severityMap[finding.severity] || 'warning', # message: { text: finding.description + ' — ' + finding.matches.join('; ') }, # locations: [{ # physicalLocation: { # artifactLocation: { uri: file.filePath.replace(/^\.\//,''), uriBaseId: '%SRCROOT%' }, # region: { startLine: 1 }, # }, # }], # })) # ), # }], # }; # fs.writeFileSync('scan-results.sarif', JSON.stringify(sarif, null, 2)); # console.log('SARIF written.'); # SCRIPT # - name: Upload SARIF to GitHub Security tab # if: always() # uses: github/codeql-action/upload-sarif@v3 # with: # sarif_file: scan-results.sarif # category: polinrider-malware-scan # ── 6. Block deployment if infected ────────────────────────────────────── - name: Enforce clean-scan gate if: steps.scanner.outputs.SCANNER_EXIT == '1' || steps.git-check.outputs.ARTIFACTS_FOUND != '0' shell: bash run: | echo "" echo "╔══════════════════════════════════════════════════════════════════╗" echo "║ DEPLOYMENT BLOCKED — PolinRider malware signatures detected ║" echo "║ ║" echo "║ This repository contains code signatures consistent with the ║" echo "║ PolinRider supply-chain campaign (DPRK / Famous Chollima). ║" echo "║ ║" echo "║ DO NOT run npm install, build, or deploy until remediated. ║" echo "║ ║" echo "║ See scan-report.json artifact for full details. ║" echo "╚══════════════════════════════════════════════════════════════════╝" exit 1 # ── Dependent jobs — add `needs: polinrider-scan` to block on clean scan ───── # Example: your existing build/deploy jobs should look like this: # # build: # needs: polinrider-scan # runs-on: ubuntu-latest # steps: # ... # # deploy: # needs: [polinrider-scan, build] # ...