@flagrix/scanner-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/dist/github/api-error.d.ts +8 -0
  4. package/dist/github/api-error.js +36 -0
  5. package/dist/github/api-error.js.map +1 -0
  6. package/dist/github/repo-scanner.d.ts +15 -0
  7. package/dist/github/repo-scanner.js +1017 -0
  8. package/dist/github/repo-scanner.js.map +1 -0
  9. package/dist/github/user-profile-ruleset.d.ts +12 -0
  10. package/dist/github/user-profile-ruleset.js +155 -0
  11. package/dist/github/user-profile-ruleset.js.map +1 -0
  12. package/dist/github/user-scanner.d.ts +11 -0
  13. package/dist/github/user-scanner.js +141 -0
  14. package/dist/github/user-scanner.js.map +1 -0
  15. package/dist/index.d.ts +10 -0
  16. package/dist/index.js +13 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/linkedin/profile-scorer.d.ts +8 -0
  19. package/dist/linkedin/profile-scorer.js +115 -0
  20. package/dist/linkedin/profile-scorer.js.map +1 -0
  21. package/dist/pdf/pdf-scanner.d.ts +36 -0
  22. package/dist/pdf/pdf-scanner.js +148 -0
  23. package/dist/pdf/pdf-scanner.js.map +1 -0
  24. package/dist/rules/rule-matcher.d.ts +3 -0
  25. package/dist/rules/rule-matcher.js +51 -0
  26. package/dist/rules/rule-matcher.js.map +1 -0
  27. package/dist/types/index.d.ts +234 -0
  28. package/dist/types/index.js +17 -0
  29. package/dist/types/index.js.map +1 -0
  30. package/dist/utils/evidence.d.ts +8 -0
  31. package/dist/utils/evidence.js +28 -0
  32. package/dist/utils/evidence.js.map +1 -0
  33. package/dist/utils/risk-calculator.d.ts +11 -0
  34. package/dist/utils/risk-calculator.js +35 -0
  35. package/dist/utils/risk-calculator.js.map +1 -0
  36. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Flagrix
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @flagrix/scanner-core
2
+
3
+ Open-source scanning engine behind [Flagrix](https://flagrix.io) — detects malware, backdoors, and supply-chain attacks in GitHub repositories and GitHub profiles.
4
+
5
+ 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.
6
+
7
+ ## What it detects
8
+
9
+ - Known malicious npm packages (active campaign IOCs, typosquats)
10
+ - Obfuscated JavaScript (hex arrays, eval chains, base64 droppers)
11
+ - Supply-chain attacks (dependency confusion, install-time scripts)
12
+ - Backdoors, reverse shells, and data-exfiltration patterns
13
+ - Crypto miners and credential/wallet stealers
14
+ - Suspicious install hooks (postinstall, curl-pipe-bash)
15
+ - Social-engineering markers and repository anomalies
16
+
17
+ Signature data lives in the sibling repo [flagrix-detection-rules](https://github.com/flagrix-io/flagrix-detection-rules).
18
+
19
+ ## Usage
20
+
21
+ ```ts
22
+ import { scanGitHubRepo, type SignatureDatabase } from "@flagrix/scanner-core"
23
+
24
+ const result = await scanGitHubRepo(
25
+ { owner: "some-org", repo: "some-repo", branch: "main", url: "https://github.com/some-org/some-repo" },
26
+ { signatures, githubToken } // token optional — raises rate limits, enables private repos
27
+ )
28
+
29
+ console.log(result.riskLevel) // "low" | "medium" | "high"
30
+ console.log(result.findings) // detailed findings with severity + evidence
31
+ console.log(result.commitSha) // the exact commit the verdict applies to
32
+ ```
33
+
34
+ Scans 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.
35
+
36
+ Also exported: `scanGitHubUser` (profile authenticity scoring) and the shared risk-calculation utilities. See [src/index.ts](src/index.ts) for the full API.
37
+
38
+ 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.
39
+
40
+ ## GitHub API rate limits
41
+
42
+ `scanGitHubRepo` and `scanGitHubUser` call the GitHub REST API directly. A single repo scan issues one tree request plus up to ~50 file-content requests. Unauthenticated, GitHub allows **60 requests/hour** — enough for a handful of scans. 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:
43
+
44
+ ```ts
45
+ await scanGitHubRepo(repo, { signatures, githubToken })
46
+ ```
47
+
48
+ ## Risk scoring
49
+
50
+ 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. `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.
51
+
52
+ ## Development
53
+
54
+ ```bash
55
+ npm install
56
+ npm run build # tsc → dist/
57
+ npm test # vitest
58
+ ```
59
+
60
+ Pure 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](CONTRIBUTING.md) to add a detector or report a false positive.
61
+
62
+ ## Disclaimer
63
+
64
+ Risk assessments are informational, not definitive malware or fraud determinations. Always verify through official channels.
65
+
66
+ ## AI Disclosure
67
+
68
+ 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.
69
+
70
+ ## License
71
+
72
+ MIT — see [LICENSE](LICENSE).
73
+
74
+ ---
75
+
76
+ *Part of the [Flagrix](https://flagrix.io) open-core security platform.*
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Turns a failed GitHub API response into an actionable error.
3
+ *
4
+ * GitHub uses 403 for several unrelated conditions (SAML/SSO enforcement,
5
+ * disabled classic PATs, rate limiting) and explains which one in the JSON
6
+ * body — so surface that instead of a bare status code.
7
+ */
8
+ export declare function githubApiError(response: Response, fallback?: string): Promise<Error>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Turns a failed GitHub API response into an actionable error.
3
+ *
4
+ * GitHub uses 403 for several unrelated conditions (SAML/SSO enforcement,
5
+ * disabled classic PATs, rate limiting) and explains which one in the JSON
6
+ * body — so surface that instead of a bare status code.
7
+ */
8
+ export async function githubApiError(response, fallback = "GitHub API error") {
9
+ let apiMessage = "";
10
+ try {
11
+ const body = (await response.json());
12
+ if (typeof body?.message === "string")
13
+ apiMessage = body.message;
14
+ }
15
+ catch {
16
+ // Non-JSON body — fall through to the status-only message.
17
+ }
18
+ if (response.status === 401) {
19
+ return new Error("GitHub token is invalid or expired — update it in Flagrix settings (401).");
20
+ }
21
+ if (response.status === 403) {
22
+ if (/saml|sso/i.test(apiMessage) || response.headers?.get("x-github-sso")) {
23
+ return new Error("This organization requires SSO authorization for your token. Open github.com/settings/tokens and use 'Configure SSO' on your token to authorize it for the organization (403).");
24
+ }
25
+ if (/personal access token/i.test(apiMessage)) {
26
+ return new Error(`This organization restricts personal access tokens — ${apiMessage} (403).`);
27
+ }
28
+ if (response.headers?.get("x-ratelimit-remaining") === "0") {
29
+ return new Error("GitHub API rate limit exceeded. Add a personal access token in Flagrix settings to raise the limit (403).");
30
+ }
31
+ }
32
+ return new Error(apiMessage
33
+ ? `${fallback}: ${response.status} — ${apiMessage}`
34
+ : `${fallback}: ${response.status}`);
35
+ }
36
+ //# sourceMappingURL=api-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-error.js","sourceRoot":"","sources":["../../src/github/api-error.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAkB,EAClB,QAAQ,GAAG,kBAAkB;IAE7B,IAAI,UAAU,GAAG,EAAE,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA0B,CAAA;QAC7D,IAAI,OAAO,IAAI,EAAE,OAAO,KAAK,QAAQ;YAAE,UAAU,GAAG,IAAI,CAAC,OAAO,CAAA;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,IAAI,KAAK,CACd,2EAA2E,CAC5E,CAAA;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1E,OAAO,IAAI,KAAK,CACd,gLAAgL,CACjL,CAAA;QACH,CAAC;QACD,IAAI,wBAAwB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9C,OAAO,IAAI,KAAK,CACd,wDAAwD,UAAU,SAAS,CAC5E,CAAA;QACH,CAAC;QACD,IAAI,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,uBAAuB,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3D,OAAO,IAAI,KAAK,CACd,2GAA2G,CAC5G,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,KAAK,CACd,UAAU;QACR,CAAC,CAAC,GAAG,QAAQ,KAAK,QAAQ,CAAC,MAAM,MAAM,UAAU,EAAE;QACnD,CAAC,CAAC,GAAG,QAAQ,KAAK,QAAQ,CAAC,MAAM,EAAE,CACtC,CAAA;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * GitHub Repository Scanner
3
+ *
4
+ * Scans GitHub repositories for malware signatures, obfuscated code,
5
+ * suspicious dependencies, and postinstall scripts.
6
+ *
7
+ * Uses GitHub API to fetch repo contents without full clone.
8
+ * No chrome.storage dependency — token and signatures are passed as options.
9
+ */
10
+ import type { GitHubFinding, GitHubRepoInfo, GitHubScanResult, RepoScanOptions } from "../types/index.js";
11
+ import { isTestFile } from "../rules/rule-matcher.js";
12
+ export declare function scanGitHubRepo(repo: GitHubRepoInfo, options: RepoScanOptions): Promise<GitHubScanResult>;
13
+ declare function detectObfuscation(content: string, filePath: string): GitHubFinding[];
14
+ declare function checkTyposquat(packageName: string): string | null;
15
+ export { checkTyposquat, detectObfuscation, isTestFile };