@flagrix/scanner-core
v0.3.1
Published
Core security scanning logic for Flagrix — GitHub repo scanner and user profile scorer
Downloads
1,075
Maintainers
Readme
@flagrix/scanner-core
Open-source scanning engine behind Flagrix — detects malware, backdoors, and supply-chain attacks in GitHub repositories and GitHub profiles.
Built after real-world fake-recruiter campaigns ("coding assignment" repos that steal wallets, SSH keys, and browser sessions) started targeting developers. Flagrix scans before you clone.
What it detects
- Known malicious npm packages (active campaign IOCs, typosquats)
- Obfuscated JavaScript (hex arrays, eval chains, base64 droppers)
- Supply-chain attacks (dependency confusion, install-time scripts)
- Backdoors, reverse shells, and data-exfiltration patterns
- Crypto miners and credential/wallet stealers
- Suspicious install hooks (postinstall, curl-pipe-bash)
- Social-engineering markers and repository anomalies
Signature data lives in the sibling repo flagrix-detection-rules.
Privacy — everything runs locally
All analysis happens on the caller's device — there is no Flagrix backend and no telemetry. The library's only network calls are to api.github.com (file contents and profile data) and api.npmjs.org (package download counts for typosquat checks). Nothing that gets scanned — file contents, repo names, results — is sent to Flagrix or any third-party server. The optional githubToken is used exclusively as an Authorization header on GitHub API requests; it is never transmitted anywhere else.
What gets scanned
A repo scan reads code files (.js, .jsx, .mjs, .cjs, .ts, .tsx, .py, .rb, .php, .go, .sh, .ps1, .psm1, .bat, .cmd, .vbs) plus dependency manifests (package.json, lockfiles, requirements.txt, setup.py, and similar), capped at 200 files and 1 MB per file per scan. Everything else — docs, images, data files, oversized blobs — is skipped, and the result says so: scanSummary.scannedFiles lists every file that was read, and scanSummary.skippedFiles lists what was skipped with a per-file reason (unsupported-type, over-file-limit, too-large, fetch-failed).
Usage
import { scanGitHubRepo, type SignatureDatabase } from "@flagrix/scanner-core"
const result = await scanGitHubRepo(
{ owner: "some-org", repo: "some-repo", branch: "main", url: "https://github.com/some-org/some-repo" },
{ signatures, githubToken } // token optional — raises rate limits, enables private repos
)
console.log(result.riskLevel) // "low" | "medium" | "high"
console.log(result.findings) // detailed findings with severity + evidence
console.log(result.commitSha) // the exact commit the verdict applies toScans are pinned: the branch is resolved to a commit SHA up front and every file is read at that SHA, so a push mid-scan (or between scan and clone) can't invalidate the verdict silently — compare commitSha against the head you actually check out.
Also exported: scanGitHubUser (profile authenticity scoring) and the shared risk-calculation utilities. See src/index.ts for the full API.
The package also ships standalone scoreLinkedInProfile and scanPdfBytes / scanPdfFromUrl scanners. These aren't wired into the current Flagrix extension (which is GitHub-only) or backed by rules in flagrix-detection-rules — they're available for anyone building on the library, but should be treated as unmaintained until that changes.
GitHub API rate limits
scanGitHubRepo and scanGitHubUser call the GitHub REST API directly. A single repo scan issues one tree request plus up to 200 file-content requests (most repos need far fewer). Unauthenticated, GitHub allows 60 requests/hour — enough for scans of small repos. Pass a githubToken (a fine-grained or classic PAT, public_repo/repo scope) in the options to raise this to 5,000 requests/hour and to scan private repositories:
await scanGitHubRepo(repo, { signatures, githubToken })Risk scoring
Findings are weighted by severity (critical 0.4, high 0.25, medium 0.15, low 0.05, info 0.01), summed, and capped at 1.0. The pre-cap sum is also returned as rawRiskScore, and factors[] carries one entry per deduped signal with the confidence-adjusted weight actually counted — factor weights always sum to rawRiskScore, so a consumer-rendered deduction breakdown reconciles with the score. getRiskLevel maps the score to a level using the shared RISK_THRESHOLDS (< 0.3 low, < 0.6 medium, otherwise high) — with one override: a single critical finding always forces high, since a lone backdoor or keylogger shouldn't average down to "review before cloning" just because nothing else in the repo was flagged. Pass findings as the scanner does (getRiskLevel(score, findings)) to get this floor; omit it to fall back to threshold-only scoring. The GitHub user scanner uses its own tuned thresholds because profile signals (account age, follower ratios) distribute differently from code findings.
Development
npm install
npm run build # tsc → dist/
npm test # vitestPure TypeScript with a single runtime dependency (franc-min for language detection). Callers inject network and storage — the primary consumer is the Flagrix Chrome extension, which currently uses the GitHub repo and profile scanners and supplies fetch results and signature data. See CONTRIBUTING.md to add a detector or report a false positive.
Disclaimer
Risk assessments are informational, not definitive malware or fraud determinations. Always verify through official channels.
AI Disclosure
This project leverages Claude AI for boilerplate generation, test-suite expansion, and optimization. All AI-generated code is strictly reviewed, refactored, and verified by human maintainers before merging.
License
MIT — see LICENSE.
Part of the Flagrix open-core security platform.
