From d5d520190699798ca6c3ea1381edbd271856601b Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 11 Mar 2026 08:22:09 +0100 Subject: [PATCH 1/5] Create entropy-scan.yml Found this tool by sheer luck? Let's see what it reveals! --- .github/workflows/entropy-scan.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/entropy-scan.yml diff --git a/.github/workflows/entropy-scan.yml b/.github/workflows/entropy-scan.yml new file mode 100644 index 0000000000..b6d573c1d9 --- /dev/null +++ b/.github/workflows/entropy-scan.yml @@ -0,0 +1,14 @@ +# .github/workflows/entropy-scan.yml +name: Entropy + Secret Scan +on: [push, pull_request] +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: trufflesecurity/trufflehog@main + with: + extra_args: --results=verified,unknown --filter-entropy=4.0 From dc11e3797dc3abb0e7065e997d169436bf69b60a Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 11 Mar 2026 09:37:16 +0100 Subject: [PATCH 2/5] Create compute-entropy.py make it beautiful --- .github/workflows/compute-entropy.py | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/compute-entropy.py diff --git a/.github/workflows/compute-entropy.py b/.github/workflows/compute-entropy.py new file mode 100644 index 0000000000..42655913bb --- /dev/null +++ b/.github/workflows/compute-entropy.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +import sys, math, json, subprocess +from collections import Counter +from pathlib import Path + +def shannon_entropy(text: str) -> float: + if not text or len(text) < 10: + return 0.0 + freq = Counter(text) + probs = [count / len(text) for count in freq.values()] + return -sum(p * math.log2(p) for p in probs if p > 0) + +# Get changed files safely for pull_request events +changed_files = [] +try: + # GitHub provides github.event.pull_request.base.sha and head.sha in the context + base_sha = subprocess.check_output(['git', 'rev-parse', 'origin/${{ github.base_ref }}'], text=True).strip() + changed_files = subprocess.check_output( + ['git', 'diff', '--name-only', base_sha, 'HEAD'], text=True + ).splitlines() +except subprocess.CalledProcessError: + # Fallback for first-time PRs or edge cases: use the merge-base or just files in HEAD + try: + changed_files = subprocess.check_output( + ['git', 'diff', '--name-only', 'HEAD~1', 'HEAD'], text=True + ).splitlines() + except subprocess.CalledProcessError: + # Last resort: all files in the repo + changed_files = subprocess.check_output(['git', 'ls-files'], text=True).splitlines() + +results = [] +total_ent = 0.0 +count = 0 + +for f in changed_files: + path = Path(f.strip()) + if not path.exists() or path.suffix in {'.png', '.jpg', '.gif', '.bin', '.lock', '.exe', '.dll', '.so'}: + continue + try: + content = path.read_text(encoding='utf-8', errors='ignore') + ent = shannon_entropy(content) + results.append(f"{f}: {ent:.3f}") + total_ent += ent + count += 1 + except Exception: + pass + +avg = round(total_ent / count, 3) if count > 0 else 0.0 + +verdict = ( + "✅ Mid-4 beauty detected (thoughtful human code!)" if 4.3 <= avg <= 4.7 else + "⚠️ Consider review — entropy outside sweet spot" if avg > 0 else + "No source files changed" +) + +with open('/tmp/beauty.json', 'w') as f: + json.dump({ + "average_entropy": avg, + "verdict": verdict, + "files": results[:20] + }, f, indent=2) + +print(f"Average entropy: {avg}") +print(verdict) From 3aa1c376605de9a16486538e03daa9b1d30545e3 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 11 Mar 2026 09:38:29 +0100 Subject: [PATCH 3/5] Create entropy-beauty-scan.yml the yml for entropy + beauty scanning --- .github/workflows/entropy-beauty-scan.yml | 72 +++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/entropy-beauty-scan.yml diff --git a/.github/workflows/entropy-beauty-scan.yml b/.github/workflows/entropy-beauty-scan.yml new file mode 100644 index 0000000000..dc04a4cbcd --- /dev/null +++ b/.github/workflows/entropy-beauty-scan.yml @@ -0,0 +1,72 @@ +name: Entropy Beauty + TruffleHog Scan + +on: [push, pull_request, release] + +permissions: + contents: read + pull-requests: write + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code (full history) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run TruffleHog + uses: trufflesecurity/trufflehog@main + with: + path: . + extra_args: --results=verified,unknown --filter-entropy=3.5 --json + + - name: Compute mid-4 beauty entropy + run: python .github/workflows/compute-entropy.py + + - name: Post summary comment (PR only) + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + + // Read TruffleHog output — it prints one JSON object per line (NDJSON) + let findings = []; + if (fs.existsSync('trufflehog.json')) { + try { + const lines = fs.readFileSync('trufflehog.json', 'utf8').trim().split('\n'); + findings = lines.map(line => { + try { return JSON.parse(line); } catch(e) { return null; } + }).filter(Boolean); + } catch(e) {} + } else { + // Fallback: the action also logs to GITHUB_STEP_SUMMARY, but we use the file from the Python step + console.log("No trufflehog.json found, using empty findings"); + } + + const beauty = JSON.parse(fs.readFileSync('/tmp/beauty.json', 'utf8')); + + let body = `## 🐷 TruffleHog + Entropy Beauty Scan\n\n`; + body += `**Average entropy of changed code:** ${beauty.average_entropy} bits/char\n`; + body += `**Verdict:** ${beauty.verdict}\n\n`; + + if (beauty.files && beauty.files.length) { + body += `**Changed files entropy:**\n\`\`\`\n${beauty.files.join('\n')}\n\`\`\`\n\n`; + } + + if (findings.length > 0) { + body += `⚠️ **TruffleHog found ${findings.length} potential issue(s)**\n`; + } else { + body += `✅ No secrets or suspicious high-entropy strings found.\n`; + } + + body += `\n*Mid-4 beauty heuristic in action — powered by our entropy chats! 😊*`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); From 8e17baf1beea6e3c012e9bd225d42e4bd179edc6 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 11 Mar 2026 09:51:46 +0100 Subject: [PATCH 4/5] Delete .github/workflows/entropy-scan.yml delete the previous half-finished scanner --- .github/workflows/entropy-scan.yml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .github/workflows/entropy-scan.yml diff --git a/.github/workflows/entropy-scan.yml b/.github/workflows/entropy-scan.yml deleted file mode 100644 index b6d573c1d9..0000000000 --- a/.github/workflows/entropy-scan.yml +++ /dev/null @@ -1,14 +0,0 @@ -# .github/workflows/entropy-scan.yml -name: Entropy + Secret Scan -on: [push, pull_request] -jobs: - scan: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: trufflesecurity/trufflehog@main - with: - extra_args: --results=verified,unknown --filter-entropy=4.0 From 64ada5dce2c16bd254481e5c58dedfffc6049638 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 11 Mar 2026 09:58:01 +0100 Subject: [PATCH 5/5] Update entropy-beauty-scan.yml try fixing merge issue postback --- .github/workflows/entropy-beauty-scan.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/entropy-beauty-scan.yml b/.github/workflows/entropy-beauty-scan.yml index dc04a4cbcd..e80695f49c 100644 --- a/.github/workflows/entropy-beauty-scan.yml +++ b/.github/workflows/entropy-beauty-scan.yml @@ -5,6 +5,7 @@ on: [push, pull_request, release] permissions: contents: read pull-requests: write + issues: write # must be at workflow level for push/merge events jobs: scan: