ossrisk 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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/dist/action/main.d.ts +2 -0
  4. package/dist/action/main.d.ts.map +1 -0
  5. package/dist/action/main.js +67 -0
  6. package/dist/action/main.js.map +1 -0
  7. package/dist/src/checkers/activity.d.ts +3 -0
  8. package/dist/src/checkers/activity.d.ts.map +1 -0
  9. package/dist/src/checkers/activity.js +49 -0
  10. package/dist/src/checkers/activity.js.map +1 -0
  11. package/dist/src/checkers/eol.d.ts +3 -0
  12. package/dist/src/checkers/eol.d.ts.map +1 -0
  13. package/dist/src/checkers/eol.js +71 -0
  14. package/dist/src/checkers/eol.js.map +1 -0
  15. package/dist/src/checkers/osv.d.ts +3 -0
  16. package/dist/src/checkers/osv.d.ts.map +1 -0
  17. package/dist/src/checkers/osv.js +71 -0
  18. package/dist/src/checkers/osv.js.map +1 -0
  19. package/dist/src/checkers/outdated.d.ts +3 -0
  20. package/dist/src/checkers/outdated.d.ts.map +1 -0
  21. package/dist/src/checkers/outdated.js +33 -0
  22. package/dist/src/checkers/outdated.js.map +1 -0
  23. package/dist/src/cli.d.ts +3 -0
  24. package/dist/src/cli.d.ts.map +1 -0
  25. package/dist/src/cli.js +79 -0
  26. package/dist/src/cli.js.map +1 -0
  27. package/dist/src/output/markdown.d.ts +3 -0
  28. package/dist/src/output/markdown.d.ts.map +1 -0
  29. package/dist/src/output/markdown.js +47 -0
  30. package/dist/src/output/markdown.js.map +1 -0
  31. package/dist/src/output/table.d.ts +3 -0
  32. package/dist/src/output/table.d.ts.map +1 -0
  33. package/dist/src/output/table.js +77 -0
  34. package/dist/src/output/table.js.map +1 -0
  35. package/dist/src/parsers/npm.d.ts +3 -0
  36. package/dist/src/parsers/npm.d.ts.map +1 -0
  37. package/dist/src/parsers/npm.js +43 -0
  38. package/dist/src/parsers/npm.js.map +1 -0
  39. package/dist/src/parsers/python.d.ts +3 -0
  40. package/dist/src/parsers/python.d.ts.map +1 -0
  41. package/dist/src/parsers/python.js +24 -0
  42. package/dist/src/parsers/python.js.map +1 -0
  43. package/dist/src/scanner.d.ts +3 -0
  44. package/dist/src/scanner.d.ts.map +1 -0
  45. package/dist/src/scanner.js +74 -0
  46. package/dist/src/scanner.js.map +1 -0
  47. package/dist/src/types.d.ts +65 -0
  48. package/dist/src/types.d.ts.map +1 -0
  49. package/dist/src/types.js +2 -0
  50. package/dist/src/types.js.map +1 -0
  51. package/package.json +51 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 depkeep
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,160 @@
1
+ # ossrisk
2
+
3
+ Scan your dependencies for long-term viability risk: **EOL versions**, **known CVEs**, and **abandonment signals**.
4
+
5
+ Supports `package.json` (npm) and `requirements.txt` (PyPI).
6
+
7
+ [![CI](https://github.com/depkeep/ossrisk/actions/workflows/ci.yml/badge.svg)](https://github.com/depkeep/ossrisk/actions/workflows/ci.yml)
8
+ [![npm](https://img.shields.io/npm/v/ossrisk)](https://www.npmjs.com/package/ossrisk)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
10
+
11
+ ---
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install -g ossrisk
17
+ ```
18
+
19
+ Or run without installing:
20
+
21
+ ```bash
22
+ npx ossrisk .
23
+ ```
24
+
25
+ ---
26
+
27
+ ## CLI usage
28
+
29
+ ```
30
+ ossrisk [path] [options]
31
+ ```
32
+
33
+ | Option | Default | Description |
34
+ |---|---|---|
35
+ | `[path]` | `.` | Path to project directory to scan |
36
+ | `-f, --format <fmt>` | `table` | Output format: `table`, `json`, `markdown` |
37
+ | `--fail-on <level>` | `high` | Exit 1 if any dep reaches this risk level (`none`\|`low`\|`medium`\|`high`\|`critical`) |
38
+ | `-c, --concurrency <n>` | `8` | Concurrent API requests per batch |
39
+ | `--no-eol` | | Skip EOL checks |
40
+ | `--no-cve` | | Skip CVE checks |
41
+ | `--no-activity` | | Skip abandonment/staleness checks |
42
+ | `--no-outdated` | | Skip latest-version checks |
43
+
44
+ ### Examples
45
+
46
+ ```bash
47
+ # Scan the current directory
48
+ ossrisk
49
+
50
+ # Scan a specific project
51
+ ossrisk /path/to/project
52
+
53
+ # Output as JSON
54
+ ossrisk . --format json
55
+
56
+ # Fail on medium risk or above
57
+ ossrisk . --fail-on medium
58
+
59
+ # Skip CVE checks, output markdown
60
+ ossrisk . --no-cve --format markdown
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Risk levels
66
+
67
+ | Level | Triggers |
68
+ |---|---|
69
+ | `critical` | CVE with CVSS ≥ 9.0 |
70
+ | `high` | CVE with CVSS 7.0–8.9, or EOL version |
71
+ | `medium` | CVE with CVSS 4.0–6.9, or no release in 24+ months (abandoned) |
72
+ | `low` | CVE with CVSS < 4.0, no release in 12–24 months (stale), or newer version available |
73
+ | `none` | No issues found |
74
+
75
+ ---
76
+
77
+ ## GitHub Actions
78
+
79
+ Add ossrisk to your CI pipeline to automatically scan dependencies on every pull request.
80
+
81
+ ```yaml
82
+ - name: Scan dependencies
83
+ uses: depkeep/ossrisk@v1
84
+ with:
85
+ fail-on: high
86
+ github-token: ${{ secrets.GITHUB_TOKEN }}
87
+ ```
88
+
89
+ When `github-token` is provided and the workflow runs on a pull request, ossrisk posts a markdown report as a PR comment.
90
+
91
+ ### Action inputs
92
+
93
+ | Input | Default | Description |
94
+ |---|---|---|
95
+ | `path` | `.` | Path to the project directory |
96
+ | `fail-on` | `high` | Exit 1 if any dep reaches this level or above |
97
+ | `no-eol` | `false` | Skip EOL checks |
98
+ | `no-cve` | `false` | Skip CVE checks |
99
+ | `no-activity` | `false` | Skip abandonment/staleness checks |
100
+ | `no-outdated` | `false` | Skip latest-version checks |
101
+ | `github-token` | | GitHub token for posting a PR comment |
102
+
103
+ ### Action outputs
104
+
105
+ | Output | Description |
106
+ |---|---|
107
+ | `risk-level` | Highest risk level found across all dependencies |
108
+
109
+ ---
110
+
111
+ ## Programmatic API
112
+
113
+ ```ts
114
+ import { scan } from 'ossrisk';
115
+
116
+ const result = await scan({
117
+ path: '/path/to/project',
118
+ format: 'json',
119
+ failOn: 'high',
120
+ concurrency: 8,
121
+ noEol: false,
122
+ noCve: false,
123
+ noActivity: false,
124
+ noOutdated: false,
125
+ });
126
+
127
+ console.log(result.summary);
128
+ // { total: 42, critical: 0, high: 1, medium: 3, low: 5, clean: 33 }
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Data sources
134
+
135
+ - **CVEs** — [OSV.dev](https://osv.dev) batch API
136
+ - **EOL dates** — [endoflife.date](https://endoflife.date) API
137
+ - **Activity** — npm registry / PyPI JSON API
138
+ - **Latest versions** — npm registry / PyPI JSON API
139
+
140
+ All checks are read-only and require no API keys.
141
+
142
+ ---
143
+
144
+ ## Contributing
145
+
146
+ ```bash
147
+ git clone https://github.com/depkeep/ossrisk.git
148
+ cd ossrisk
149
+ npm install
150
+ npm test # run tests
151
+ npm run dev . # run CLI from source
152
+ ```
153
+
154
+ Before submitting a PR, run `npm run build` and commit the updated `dist/` so the GitHub Action stays functional.
155
+
156
+ ---
157
+
158
+ ## License
159
+
160
+ MIT © [DepKeep](https://depkeep.com)
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../action/main.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { appendFileSync, readFileSync } from 'fs';
2
+ import { resolve } from 'path';
3
+ import { scan } from '../src/scanner.js';
4
+ import { renderMarkdown } from '../src/output/markdown.js';
5
+ const RISK_ORDER = ['none', 'low', 'medium', 'high', 'critical'];
6
+ function getInput(name, fallback = '') {
7
+ return process.env[`INPUT_${name.toUpperCase().replace(/-/g, '_')}`] ?? fallback;
8
+ }
9
+ function setOutput(name, value) {
10
+ const file = process.env.GITHUB_OUTPUT;
11
+ if (file)
12
+ appendFileSync(file, `${name}=${value}\n`);
13
+ }
14
+ async function postPrComment(token, body) {
15
+ const { GITHUB_REPOSITORY, GITHUB_EVENT_PATH } = process.env;
16
+ if (!GITHUB_REPOSITORY || !GITHUB_EVENT_PATH)
17
+ return;
18
+ const event = JSON.parse(readFileSync(GITHUB_EVENT_PATH, 'utf-8'));
19
+ const prNumber = event.pull_request?.number;
20
+ if (!prNumber)
21
+ return;
22
+ const [owner, repo] = GITHUB_REPOSITORY.split('/');
23
+ await fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments`, {
24
+ method: 'POST',
25
+ headers: {
26
+ Authorization: `Bearer ${token}`,
27
+ 'Content-Type': 'application/json',
28
+ },
29
+ body: JSON.stringify({ body }),
30
+ });
31
+ }
32
+ async function run() {
33
+ const opts = {
34
+ path: resolve(getInput('path', '.')),
35
+ format: 'markdown',
36
+ failOn: getInput('fail-on', 'high'),
37
+ concurrency: 8,
38
+ noEol: getInput('no-eol') === 'true',
39
+ noCve: getInput('no-cve') === 'true',
40
+ noActivity: getInput('no-activity') === 'true',
41
+ noOutdated: getInput('no-outdated') === 'true',
42
+ };
43
+ try {
44
+ const result = await scan(opts);
45
+ const report = renderMarkdown(result);
46
+ console.log(report);
47
+ const token = getInput('github-token');
48
+ if (token && process.env.GITHUB_EVENT_NAME === 'pull_request') {
49
+ await postPrComment(token, report);
50
+ }
51
+ setOutput('risk-level', result.results[0]?.riskLevel ?? 'none');
52
+ const threshold = RISK_ORDER.indexOf(opts.failOn);
53
+ if (threshold > 0) {
54
+ const breached = result.results.some(r => RISK_ORDER.indexOf(r.riskLevel) >= threshold);
55
+ if (breached) {
56
+ console.error(`ossrisk: one or more dependencies are at risk level "${opts.failOn}" or above`);
57
+ process.exit(1);
58
+ }
59
+ }
60
+ }
61
+ catch (err) {
62
+ console.error('ossrisk action failed:', err.message);
63
+ process.exit(2);
64
+ }
65
+ }
66
+ run();
67
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../action/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AbandonedSignal, Dependency, StaleSignal } from '../types.js';
2
+ export declare function checkActivity(dep: Dependency): Promise<(AbandonedSignal | StaleSignal)[]>;
3
+ //# sourceMappingURL=activity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../../src/checkers/activity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA+B5E,wBAAsB,aAAa,CACjC,GAAG,EAAE,UAAU,GACd,OAAO,CAAC,CAAC,eAAe,GAAG,WAAW,CAAC,EAAE,CAAC,CAuB5C"}
@@ -0,0 +1,49 @@
1
+ const ABANDONED_MONTHS = 24;
2
+ const STALE_MONTHS = 12;
3
+ function monthsSince(date) {
4
+ return (Date.now() - date.getTime()) / (1000 * 60 * 60 * 24 * 30.44);
5
+ }
6
+ async function lastNpmRelease(name) {
7
+ const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}`, {
8
+ headers: { Accept: 'application/json' },
9
+ });
10
+ if (!res.ok)
11
+ return null;
12
+ const data = await res.json();
13
+ const modified = data.time?.modified;
14
+ return modified ? new Date(modified) : null;
15
+ }
16
+ async function lastPypiRelease(name) {
17
+ const res = await fetch(`https://pypi.org/pypi/${encodeURIComponent(name)}/json`);
18
+ if (!res.ok)
19
+ return null;
20
+ const data = await res.json();
21
+ const files = data.releases[data.info.version];
22
+ if (!files?.length)
23
+ return null;
24
+ return new Date(files[0].upload_time);
25
+ }
26
+ export async function checkActivity(dep) {
27
+ try {
28
+ let last = null;
29
+ if (dep.ecosystem === 'npm')
30
+ last = await lastNpmRelease(dep.name);
31
+ if (dep.ecosystem === 'pypi')
32
+ last = await lastPypiRelease(dep.name);
33
+ if (!last)
34
+ return [];
35
+ const months = Math.floor(monthsSince(last));
36
+ const dateStr = last.toISOString().split('T')[0];
37
+ if (months >= ABANDONED_MONTHS) {
38
+ return [{ type: 'abandoned', lastReleaseDate: dateStr, monthsSince: months }];
39
+ }
40
+ if (months >= STALE_MONTHS) {
41
+ return [{ type: 'stale', lastReleaseDate: dateStr, monthsSince: months }];
42
+ }
43
+ }
44
+ catch {
45
+ // Registry unreachable — not a failure condition
46
+ }
47
+ return [];
48
+ }
49
+ //# sourceMappingURL=activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../../src/checkers/activity.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,SAAS,WAAW,CAAC,IAAU;IAC7B,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;AACvE,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,8BAA8B,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE;QAChF,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;KACxC,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAuC,CAAC;IACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;IACrC,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,yBAAyB,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClF,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAG1B,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK,EAAE,MAAM;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAe;IAEf,IAAI,CAAC;QACH,IAAI,IAAI,GAAgB,IAAI,CAAC;QAE7B,IAAI,GAAG,CAAC,SAAS,KAAK,KAAK;YAAG,IAAI,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpE,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM;YAAE,IAAI,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAErE,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QAErB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjD,IAAI,MAAM,IAAI,gBAAgB,EAAE,CAAC;YAC/B,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAChF,CAAC;QACD,IAAI,MAAM,IAAI,YAAY,EAAE,CAAC;YAC3B,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Dependency, EolSignal } from '../types.js';
2
+ export declare function checkEol(dep: Dependency): Promise<EolSignal[]>;
3
+ //# sourceMappingURL=eol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eol.d.ts","sourceRoot":"","sources":["../../../src/checkers/eol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAgDzD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CA2BpE"}
@@ -0,0 +1,71 @@
1
+ // Maps package names → endoflife.date product slugs.
2
+ // Only packages/runtimes that have formal EOL policies are included.
3
+ const EOL_MAP = {
4
+ // Runtimes
5
+ 'node': 'nodejs',
6
+ 'nodejs': 'nodejs',
7
+ 'python': 'python',
8
+ 'ruby': 'ruby',
9
+ 'php': 'php',
10
+ 'golang': 'go',
11
+ // Frameworks
12
+ 'django': 'django',
13
+ 'rails': 'rails',
14
+ 'laravel': 'laravel',
15
+ 'symfony': 'symfony',
16
+ 'spring-boot': 'spring-boot',
17
+ 'angular': 'angular',
18
+ '@angular/core': 'angular',
19
+ 'bootstrap': 'bootstrap',
20
+ // Databases (client package name → server product)
21
+ 'pg': 'postgresql',
22
+ 'mysql': 'mysql',
23
+ 'mysql2': 'mysql',
24
+ 'mongodb': 'mongodb',
25
+ // Others
26
+ 'wordpress': 'wordpress',
27
+ 'drupal': 'drupal',
28
+ 'magento': 'magento-2',
29
+ 'nextjs': 'nextjs',
30
+ 'next': 'nextjs',
31
+ 'nuxt': 'nuxtjs',
32
+ };
33
+ function candidateCycles(version) {
34
+ const parts = version.split('.').filter(p => /^\d+$/.test(p));
35
+ const cycles = [];
36
+ if (parts.length >= 2)
37
+ cycles.push(`${parts[0]}.${parts[1]}`);
38
+ if (parts.length >= 1)
39
+ cycles.push(parts[0]);
40
+ return cycles;
41
+ }
42
+ export async function checkEol(dep) {
43
+ const product = EOL_MAP[dep.name];
44
+ if (!product)
45
+ return [];
46
+ try {
47
+ const res = await fetch(`https://endoflife.date/api/${product}.json`);
48
+ if (!res.ok)
49
+ return [];
50
+ const cycles = await res.json();
51
+ const candidates = candidateCycles(dep.version);
52
+ for (const candidate of candidates) {
53
+ const cycle = cycles.find(c => c.cycle === candidate);
54
+ if (!cycle)
55
+ continue;
56
+ const { eol } = cycle;
57
+ if (eol === false)
58
+ return []; // still supported
59
+ if (eol === true)
60
+ return [{ type: 'eol', cycle: candidate, eolDate: 'unknown' }];
61
+ if (new Date(eol) <= new Date()) {
62
+ return [{ type: 'eol', cycle: candidate, eolDate: eol }];
63
+ }
64
+ }
65
+ }
66
+ catch {
67
+ // Silently skip — endoflife.date may not have data for every product
68
+ }
69
+ return [];
70
+ }
71
+ //# sourceMappingURL=eol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eol.js","sourceRoot":"","sources":["../../../src/checkers/eol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { CveSignal, Dependency } from '../types.js';
2
+ export declare function checkCvesBatch(deps: Dependency[]): Promise<Map<string, CveSignal[]>>;
3
+ //# sourceMappingURL=osv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osv.d.ts","sourceRoot":"","sources":["../../../src/checkers/osv.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAa,MAAM,aAAa,CAAC;AA0CpE,wBAAsB,cAAc,CAClC,IAAI,EAAE,UAAU,EAAE,GACjB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAqCnC"}
@@ -0,0 +1,71 @@
1
+ const OSV_BATCH_API = 'https://api.osv.dev/v1/querybatch';
2
+ const BATCH_SIZE = 100;
3
+ const ECOSYSTEM_MAP = {
4
+ npm: 'npm',
5
+ pypi: 'PyPI',
6
+ maven: 'Maven',
7
+ cargo: 'crates.io',
8
+ go: 'Go',
9
+ };
10
+ function cvssToLevel(score) {
11
+ const n = parseFloat(score);
12
+ if (n >= 9.0)
13
+ return 'critical';
14
+ if (n >= 7.0)
15
+ return 'high';
16
+ if (n >= 4.0)
17
+ return 'medium';
18
+ return 'low';
19
+ }
20
+ function vulnSeverity(v) {
21
+ for (const s of v.severity ?? []) {
22
+ if (s.type === 'CVSS_V3' || s.type === 'CVSS_V2')
23
+ return cvssToLevel(s.score);
24
+ }
25
+ const ds = v.database_specific?.severity?.toUpperCase();
26
+ if (ds === 'CRITICAL')
27
+ return 'critical';
28
+ if (ds === 'HIGH')
29
+ return 'high';
30
+ if (ds === 'MODERATE' || ds === 'MEDIUM')
31
+ return 'medium';
32
+ if (ds === 'LOW')
33
+ return 'low';
34
+ return 'medium';
35
+ }
36
+ export async function checkCvesBatch(deps) {
37
+ const results = new Map();
38
+ for (let i = 0; i < deps.length; i += BATCH_SIZE) {
39
+ const batch = deps.slice(i, i + BATCH_SIZE);
40
+ try {
41
+ const res = await fetch(OSV_BATCH_API, {
42
+ method: 'POST',
43
+ headers: { 'Content-Type': 'application/json' },
44
+ body: JSON.stringify({
45
+ queries: batch.map(d => ({
46
+ version: d.version,
47
+ package: { name: d.name, ecosystem: ECOSYSTEM_MAP[d.ecosystem] ?? d.ecosystem },
48
+ })),
49
+ }),
50
+ });
51
+ if (!res.ok)
52
+ continue;
53
+ const data = await res.json();
54
+ for (let j = 0; j < batch.length; j++) {
55
+ const dep = batch[j];
56
+ const vulns = data.results[j]?.vulns ?? [];
57
+ results.set(`${dep.name}@${dep.version}`, vulns.map(v => ({
58
+ type: 'cve',
59
+ id: v.id,
60
+ severity: vulnSeverity(v),
61
+ summary: v.summary ?? 'No description available',
62
+ })));
63
+ }
64
+ }
65
+ catch {
66
+ // Network errors: skip batch, leave entries absent (treated as no CVEs)
67
+ }
68
+ }
69
+ return results;
70
+ }
71
+ //# sourceMappingURL=osv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osv.js","sourceRoot":"","sources":["../../../src/checkers/osv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Dependency, OutdatedSignal } from '../types.js';
2
+ export declare function checkOutdated(dep: Dependency): Promise<OutdatedSignal[]>;
3
+ //# sourceMappingURL=outdated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outdated.d.ts","sourceRoot":"","sources":["../../../src/checkers/outdated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAmB9D,wBAAsB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAkB9E"}
@@ -0,0 +1,33 @@
1
+ async function latestNpmVersion(name) {
2
+ const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}/latest`, { headers: { Accept: 'application/json' } });
3
+ if (!res.ok)
4
+ return null;
5
+ const data = await res.json();
6
+ return data.version ?? null;
7
+ }
8
+ async function latestPypiVersion(name) {
9
+ const res = await fetch(`https://pypi.org/pypi/${encodeURIComponent(name)}/json`);
10
+ if (!res.ok)
11
+ return null;
12
+ const data = await res.json();
13
+ return data.info?.version ?? null;
14
+ }
15
+ export async function checkOutdated(dep) {
16
+ try {
17
+ let latest = null;
18
+ if (dep.ecosystem === 'npm') {
19
+ latest = await latestNpmVersion(dep.name);
20
+ }
21
+ else if (dep.ecosystem === 'pypi') {
22
+ latest = await latestPypiVersion(dep.name);
23
+ }
24
+ if (!latest || latest === dep.version)
25
+ return [];
26
+ return [{ type: 'outdated', latestVersion: latest }];
27
+ }
28
+ catch {
29
+ // Registry unreachable — not a failure condition
30
+ }
31
+ return [];
32
+ }
33
+ //# sourceMappingURL=outdated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outdated.js","sourceRoot":"","sources":["../../../src/checkers/outdated.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,gBAAgB,CAAC,IAAY;IAC1C,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,8BAA8B,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAC/D,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAC5C,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAA0B,CAAC;IACtD,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;AAC9B,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAC3C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,yBAAyB,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClF,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAqC,CAAC;IACjE,OAAO,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAe;IACjD,IAAI,CAAC;QACH,IAAI,MAAM,GAAkB,IAAI,CAAC;QAEjC,IAAI,GAAG,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;YAC5B,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;YACpC,MAAM,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC;QAED,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,GAAG,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAEjD,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'fs';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join, resolve } from 'path';
5
+ import { Command } from 'commander';
6
+ import { scan } from './scanner.js';
7
+ import { renderTable } from './output/table.js';
8
+ import { renderMarkdown } from './output/markdown.js';
9
+ function readVersion() {
10
+ const dir = dirname(fileURLToPath(import.meta.url));
11
+ // '../package.json' works when running via `tsx src/cli.ts` (dev)
12
+ // '../../package.json' works from the compiled `dist/src/cli.js`
13
+ for (const rel of ['../package.json', '../../package.json']) {
14
+ try {
15
+ const p = JSON.parse(readFileSync(join(dir, rel), 'utf-8'));
16
+ if (p.name === 'ossrisk' && p.version)
17
+ return p.version;
18
+ }
19
+ catch { /* skip */ }
20
+ }
21
+ return '0.0.0';
22
+ }
23
+ const RISK_ORDER = ['none', 'low', 'medium', 'high', 'critical'];
24
+ const program = new Command();
25
+ program
26
+ .name('ossrisk')
27
+ .description('Scan dependencies for long-term viability risk:\n' +
28
+ 'EOL versions, known CVEs, and abandonment signals.\n\n' +
29
+ 'Supports: package.json (npm), requirements.txt (PyPI)')
30
+ .version(readVersion());
31
+ program
32
+ .argument('[path]', 'Path to project directory to scan', '.')
33
+ .option('-f, --format <fmt>', 'Output format: table | json | markdown', 'table')
34
+ .option('--fail-on <level>', 'Exit 1 if any dep reaches this level or above (none|low|medium|high|critical)', 'high')
35
+ .option('-c, --concurrency <n>', 'Concurrent API requests per batch', '8')
36
+ .option('--no-eol', 'Skip EOL checks')
37
+ .option('--no-cve', 'Skip CVE checks')
38
+ .option('--no-activity', 'Skip abandonment/staleness checks')
39
+ .option('--no-outdated', 'Skip latest-version checks')
40
+ .action(async (pathArg, options) => {
41
+ const opts = {
42
+ path: resolve(pathArg),
43
+ format: options.format,
44
+ failOn: options.failOn,
45
+ concurrency: parseInt(options.concurrency, 10) || 8,
46
+ noEol: !options.eol,
47
+ noCve: !options.cve,
48
+ noActivity: !options.activity,
49
+ noOutdated: !options.outdated,
50
+ };
51
+ try {
52
+ if (opts.format === 'table') {
53
+ process.stdout.write('\r Scanning…');
54
+ }
55
+ const result = await scan(opts);
56
+ if (opts.format === 'json') {
57
+ console.log(JSON.stringify(result, null, 2));
58
+ }
59
+ else if (opts.format === 'markdown') {
60
+ console.log(renderMarkdown(result));
61
+ }
62
+ else {
63
+ process.stdout.write('\r' + ' '.repeat(20) + '\r');
64
+ console.log(renderTable(result));
65
+ }
66
+ if (opts.failOn !== 'none') {
67
+ const threshold = RISK_ORDER.indexOf(opts.failOn);
68
+ const breached = result.results.some(r => RISK_ORDER.indexOf(r.riskLevel) >= threshold);
69
+ if (breached)
70
+ process.exit(1);
71
+ }
72
+ }
73
+ catch (err) {
74
+ console.error(`\n error: ${err.message}`);
75
+ process.exit(2);
76
+ }
77
+ });
78
+ program.parse();
79
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ScanResult } from '../types.js';
2
+ export declare function renderMarkdown(result: ScanResult): string;
3
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/output/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,aAAa,CAAC;AAUzD,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA6CzD"}
@@ -0,0 +1,47 @@
1
+ const EMOJI = {
2
+ critical: '🔴',
3
+ high: '🟠',
4
+ medium: '🟡',
5
+ low: '🔵',
6
+ none: '🟢',
7
+ };
8
+ export function renderMarkdown(result) {
9
+ const { summary } = result;
10
+ const lines = [];
11
+ lines.push('## ossrisk — Dependency Health Report');
12
+ lines.push('');
13
+ lines.push(`Scanned \`${result.manifest}\` · ${result.scannedAt}`);
14
+ lines.push('');
15
+ lines.push(`**${summary.total} dependencies** — ` +
16
+ `${EMOJI.critical} ${summary.critical} critical · ` +
17
+ `${EMOJI.high} ${summary.high} high · ` +
18
+ `${EMOJI.medium} ${summary.medium} medium · ` +
19
+ `${EMOJI.low} ${summary.low} low · ` +
20
+ `${EMOJI.none} ${summary.clean} clean`);
21
+ lines.push('');
22
+ const risky = result.results.filter(r => r.riskLevel !== 'none');
23
+ if (risky.length === 0) {
24
+ lines.push('✅ All dependencies are healthy.');
25
+ return lines.join('\n');
26
+ }
27
+ lines.push('| Package | Version | Risk | Details |');
28
+ lines.push('|---------|---------|------|---------|');
29
+ for (const dep of risky) {
30
+ const details = dep.signals.map(s => {
31
+ if (s.type === 'cve') {
32
+ return `[${s.id}](https://osv.dev/vulnerability/${s.id}) — ${s.summary}`;
33
+ }
34
+ if (s.type === 'eol')
35
+ return `EOL since ${s.eolDate} (cycle ${s.cycle})`;
36
+ if (s.type === 'abandoned')
37
+ return `No release in ${s.monthsSince} months (last: ${s.lastReleaseDate})`;
38
+ if (s.type === 'stale')
39
+ return `Last release ${s.monthsSince} months ago (${s.lastReleaseDate})`;
40
+ if (s.type === 'outdated')
41
+ return `Newer version available: ${s.latestVersion}`;
42
+ }).join('<br>');
43
+ lines.push(`| \`${dep.name}\` | \`${dep.version}\` | ${EMOJI[dep.riskLevel]} ${dep.riskLevel} | ${details} |`);
44
+ }
45
+ return lines.join('\n');
46
+ }
47
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/output/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ScanResult } from '../types.js';
2
+ export declare function renderTable(result: ScanResult): string;
3
+ //# sourceMappingURL=table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/output/table.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,aAAa,CAAC;AAqCzD,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAmDtD"}
@@ -0,0 +1,77 @@
1
+ const C = {
2
+ reset: '\x1b[0m',
3
+ bold: '\x1b[1m',
4
+ dim: '\x1b[2m',
5
+ red: '\x1b[31m',
6
+ yellow: '\x1b[33m',
7
+ blue: '\x1b[34m',
8
+ cyan: '\x1b[36m',
9
+ green: '\x1b[32m',
10
+ };
11
+ const LEVEL_COLOR = {
12
+ critical: C.red,
13
+ high: C.yellow,
14
+ medium: C.blue,
15
+ low: C.cyan,
16
+ none: C.green,
17
+ };
18
+ const LEVEL_ICON = {
19
+ critical: '✖',
20
+ high: '▲',
21
+ medium: '●',
22
+ low: '◆',
23
+ none: '✔',
24
+ };
25
+ function col(text, level) {
26
+ return `${LEVEL_COLOR[level]}${text}${C.reset}`;
27
+ }
28
+ function pad(s, n) {
29
+ return s.length >= n ? s.slice(0, n - 1) + '…' : s.padEnd(n);
30
+ }
31
+ export function renderTable(result) {
32
+ const lines = [];
33
+ lines.push('');
34
+ lines.push(`${C.bold}ossrisk${C.reset} ${C.dim}${result.manifest}${C.reset}`);
35
+ lines.push(`${C.dim}${result.scannedAt}${C.reset}`);
36
+ lines.push('');
37
+ const nameW = Math.min(42, Math.max(20, ...result.results.map(r => r.name.length + 2)));
38
+ lines.push(` ${C.bold}${'Package'.padEnd(nameW)}${'Version'.padEnd(14)}${'Risk'.padEnd(11)}Details${C.reset}`);
39
+ lines.push(' ' + '─'.repeat(84));
40
+ for (const dep of result.results) {
41
+ const details = dep.signals.map(s => {
42
+ if (s.type === 'cve')
43
+ return `${s.id} (${s.severity})`;
44
+ if (s.type === 'eol')
45
+ return `EOL ${s.eolDate}`;
46
+ if (s.type === 'abandoned')
47
+ return `no release in ${s.monthsSince}mo`;
48
+ if (s.type === 'stale')
49
+ return `last release ${s.monthsSince}mo ago`;
50
+ if (s.type === 'outdated')
51
+ return `latest ${s.latestVersion}`;
52
+ }).join(' ');
53
+ const isClean = dep.riskLevel === 'none';
54
+ const icon = LEVEL_ICON[dep.riskLevel];
55
+ const name = pad(dep.name, nameW - 2);
56
+ lines.push(isClean
57
+ ? ` ${C.dim}${icon} ${name} ${dep.version.padEnd(12)} ${'—'.padEnd(9)} —${C.reset}`
58
+ : ` ${col(`${icon} ${name}`, dep.riskLevel)} ` +
59
+ `${dep.version.padEnd(12)} ` +
60
+ `${col(dep.riskLevel.padEnd(9), dep.riskLevel)} ` +
61
+ `${C.dim}${details}${C.reset}`);
62
+ }
63
+ const s = result.summary;
64
+ const parts = [
65
+ `${C.bold}${s.total}${C.reset} deps`,
66
+ s.critical ? col(`${s.critical} critical`, 'critical') : '',
67
+ s.high ? col(`${s.high} high`, 'high') : '',
68
+ s.medium ? col(`${s.medium} medium`, 'medium') : '',
69
+ s.low ? col(`${s.low} low`, 'low') : '',
70
+ col(`${s.clean} clean`, 'none'),
71
+ ].filter(Boolean);
72
+ lines.push('');
73
+ lines.push(` ${parts.join(' · ')}`);
74
+ lines.push('');
75
+ return lines.join('\n');
76
+ }
77
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../../../src/output/table.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Dependency } from '../types.js';
2
+ export declare function parseNpm(dir: string): Promise<Dependency[]>;
3
+ //# sourceMappingURL=npm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../../src/parsers/npm.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+C9C,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAEjE"}
@@ -0,0 +1,43 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ function cleanVersion(raw) {
4
+ // Strip range operators (^, ~, >=, etc.) and take the first concrete version
5
+ return raw
6
+ .replace(/^[^0-9]*/, '')
7
+ .split(/\s+/)[0]
8
+ .split('||')[0]
9
+ .trim();
10
+ }
11
+ async function fromLockfile(dir) {
12
+ try {
13
+ const content = await readFile(join(dir, 'package-lock.json'), 'utf-8');
14
+ const lock = JSON.parse(content);
15
+ if (!lock.packages)
16
+ return null;
17
+ return Object.entries(lock.packages)
18
+ .filter(([name, pkg]) => name !== '' && !pkg.dev && pkg.version)
19
+ .map(([name, pkg]) => ({
20
+ name: name.replace(/^node_modules\//, ''),
21
+ version: pkg.version,
22
+ ecosystem: 'npm',
23
+ }));
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ async function fromPackageJson(dir) {
30
+ const content = await readFile(join(dir, 'package.json'), 'utf-8');
31
+ const pkg = JSON.parse(content);
32
+ return Object.entries(pkg.dependencies ?? {})
33
+ .map(([name, version]) => ({
34
+ name,
35
+ version: cleanVersion(version),
36
+ ecosystem: 'npm',
37
+ }))
38
+ .filter(d => d.version && !d.version.includes('github') && !d.version.startsWith('file:'));
39
+ }
40
+ export async function parseNpm(dir) {
41
+ return (await fromLockfile(dir)) ?? (await fromPackageJson(dir));
42
+ }
43
+ //# sourceMappingURL=npm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm.js","sourceRoot":"","sources":["../../../src/parsers/npm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,SAAS,YAAY,CAAC,GAAW;IAC/B,6EAA6E;IAC7E,OAAO,GAAG;SACP,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACf,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACd,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAE9B,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAEhC,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;aACjC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC;aAC/D,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACrB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;YACzC,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,SAAS,EAAE,KAAc;SAC1B,CAAC,CAAC,CAAC;IACR,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW;IACxC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAE7B,CAAC;IAEF,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,IAAI;QACJ,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC;QAC9B,SAAS,EAAE,KAAc;KAC1B,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAW;IACxC,OAAO,CAAC,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Dependency } from '../types.js';
2
+ export declare function parsePython(dir: string): Promise<Dependency[]>;
3
+ //# sourceMappingURL=python.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../../src/parsers/python.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAO9C,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAmBpE"}
@@ -0,0 +1,24 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ function cleanVersion(spec) {
4
+ const match = spec.match(/[=~><!]+\s*([0-9][0-9a-zA-Z._-]*)/);
5
+ return match ? match[1] : spec.trim();
6
+ }
7
+ export async function parsePython(dir) {
8
+ const content = await readFile(join(dir, 'requirements.txt'), 'utf-8');
9
+ const deps = [];
10
+ for (const raw of content.split('\n')) {
11
+ const line = raw.split('#')[0].trim();
12
+ if (!line || line.startsWith('-'))
13
+ continue;
14
+ // e.g. "Django==4.2.0", "requests>=2.28.0", "flask~=2.3"
15
+ const match = line.match(/^([a-zA-Z0-9_.-]+)\s*([=~><!][=~>!]?\s*[0-9][0-9a-zA-Z._-]*)?/);
16
+ if (!match)
17
+ continue;
18
+ const name = match[1].toLowerCase().replace(/_/g, '-');
19
+ const version = match[2] ? cleanVersion(match[2]) : '0.0.0';
20
+ deps.push({ name, version, ecosystem: 'pypi' });
21
+ }
22
+ return deps;
23
+ }
24
+ //# sourceMappingURL=python.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.js","sourceRoot":"","sources":["../../../src/parsers/python.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC9D,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW;IAC3C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,EAAE,OAAO,CAAC,CAAC;IACvE,MAAM,IAAI,GAAiB,EAAE,CAAC;IAE9B,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAE5C,yDAAyD;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;QAC1F,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAE5D,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ScanOptions, ScanResult } from './types.js';
2
+ export declare function scan(opts: ScanOptions): Promise<ScanResult>;
3
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAMV,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AAyCpB,wBAAsB,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA+CjE"}
@@ -0,0 +1,74 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { checkCvesBatch } from './checkers/osv.js';
4
+ import { checkActivity } from './checkers/activity.js';
5
+ import { checkEol } from './checkers/eol.js';
6
+ import { checkOutdated } from './checkers/outdated.js';
7
+ import { parseNpm } from './parsers/npm.js';
8
+ import { parsePython } from './parsers/python.js';
9
+ const RISK_ORDER = ['none', 'low', 'medium', 'high', 'critical'];
10
+ function signalRisk(s) {
11
+ switch (s.type) {
12
+ case 'cve': return s.severity;
13
+ case 'eol': return 'high';
14
+ case 'abandoned': return 'medium';
15
+ case 'stale': return 'low';
16
+ case 'outdated': return 'low';
17
+ }
18
+ }
19
+ function maxRisk(signals) {
20
+ return signals.reduce((max, s) => {
21
+ const level = signalRisk(s);
22
+ return RISK_ORDER.indexOf(level) > RISK_ORDER.indexOf(max) ? level : max;
23
+ }, 'none');
24
+ }
25
+ async function detectAndParse(dir) {
26
+ if (existsSync(join(dir, 'package.json'))) {
27
+ const deps = await parseNpm(dir);
28
+ return { deps, manifest: join(dir, 'package.json') };
29
+ }
30
+ if (existsSync(join(dir, 'requirements.txt'))) {
31
+ const deps = await parsePython(dir);
32
+ return { deps, manifest: join(dir, 'requirements.txt') };
33
+ }
34
+ throw new Error('No supported manifest found. Supported: package.json, requirements.txt');
35
+ }
36
+ export async function scan(opts) {
37
+ const { deps, manifest } = await detectAndParse(opts.path);
38
+ // CVEs: one batched API call for all deps
39
+ const cveMap = opts.noCve
40
+ ? new Map()
41
+ : await checkCvesBatch(deps);
42
+ // EOL + activity: per-dep, run concurrently in controlled batches
43
+ const results = [];
44
+ for (let i = 0; i < deps.length; i += opts.concurrency) {
45
+ const batch = deps.slice(i, i + opts.concurrency);
46
+ const batchResults = await Promise.all(batch.map(async (dep) => {
47
+ const signals = [
48
+ ...(cveMap.get(`${dep.name}@${dep.version}`) ?? []),
49
+ ...(!opts.noEol ? await checkEol(dep) : []),
50
+ ...(!opts.noActivity ? await checkActivity(dep) : []),
51
+ ...(!opts.noOutdated ? await checkOutdated(dep) : []),
52
+ ];
53
+ return {
54
+ name: dep.name,
55
+ version: dep.version,
56
+ ecosystem: dep.ecosystem,
57
+ riskLevel: maxRisk(signals),
58
+ signals,
59
+ };
60
+ }));
61
+ results.push(...batchResults);
62
+ }
63
+ results.sort((a, b) => RISK_ORDER.indexOf(b.riskLevel) - RISK_ORDER.indexOf(a.riskLevel));
64
+ const summary = {
65
+ total: results.length,
66
+ critical: results.filter(r => r.riskLevel === 'critical').length,
67
+ high: results.filter(r => r.riskLevel === 'high').length,
68
+ medium: results.filter(r => r.riskLevel === 'medium').length,
69
+ low: results.filter(r => r.riskLevel === 'low').length,
70
+ clean: results.filter(r => r.riskLevel === 'none').length,
71
+ };
72
+ return { scannedAt: new Date().toISOString(), manifest, results, summary };
73
+ }
74
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ export type Ecosystem = 'npm' | 'pypi' | 'maven' | 'cargo' | 'go';
2
+ export type RiskLevel = 'none' | 'low' | 'medium' | 'high' | 'critical';
3
+ export interface Dependency {
4
+ name: string;
5
+ version: string;
6
+ ecosystem: Ecosystem;
7
+ }
8
+ export interface CveSignal {
9
+ type: 'cve';
10
+ id: string;
11
+ severity: RiskLevel;
12
+ summary: string;
13
+ }
14
+ export interface EolSignal {
15
+ type: 'eol';
16
+ cycle: string;
17
+ eolDate: string;
18
+ }
19
+ export interface AbandonedSignal {
20
+ type: 'abandoned';
21
+ lastReleaseDate: string;
22
+ monthsSince: number;
23
+ }
24
+ export interface StaleSignal {
25
+ type: 'stale';
26
+ lastReleaseDate: string;
27
+ monthsSince: number;
28
+ }
29
+ export interface OutdatedSignal {
30
+ type: 'outdated';
31
+ latestVersion: string;
32
+ }
33
+ export type RiskSignal = CveSignal | EolSignal | AbandonedSignal | StaleSignal | OutdatedSignal;
34
+ export interface DependencyResult {
35
+ name: string;
36
+ version: string;
37
+ ecosystem: Ecosystem;
38
+ riskLevel: RiskLevel;
39
+ signals: RiskSignal[];
40
+ }
41
+ export interface ScanSummary {
42
+ total: number;
43
+ critical: number;
44
+ high: number;
45
+ medium: number;
46
+ low: number;
47
+ clean: number;
48
+ }
49
+ export interface ScanResult {
50
+ scannedAt: string;
51
+ manifest: string;
52
+ results: DependencyResult[];
53
+ summary: ScanSummary;
54
+ }
55
+ export interface ScanOptions {
56
+ path: string;
57
+ format: 'table' | 'json' | 'markdown';
58
+ failOn: RiskLevel | 'none';
59
+ concurrency: number;
60
+ noEol: boolean;
61
+ noCve: boolean;
62
+ noActivity: boolean;
63
+ noOutdated: boolean;
64
+ }
65
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;AAElE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAExE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,KAAK,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,eAAe,GAAG,WAAW,GAAG,cAAc,CAAC;AAEhG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;IACtC,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "ossrisk",
3
+ "version": "0.1.0",
4
+ "description": "Scan dependencies for long-term viability risk: EOL versions, CVEs, and abandonment signals",
5
+ "type": "module",
6
+ "bin": {
7
+ "ossrisk": "./dist/src/cli.js"
8
+ },
9
+ "main": "./dist/src/scanner.js",
10
+ "types": "./dist/src/scanner.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/src/scanner.d.ts",
14
+ "default": "./dist/src/scanner.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist/"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "dev": "tsx src/cli.ts",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "prepare": "npm run build",
26
+ "prepublishOnly": "npm test"
27
+ },
28
+ "keywords": ["security", "dependencies", "cve", "eol", "supply-chain", "sbom", "cli"],
29
+ "author": "DepKeep <hello@depkeep.com>",
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "commander": "^14.0.3"
33
+ },
34
+ "devDependencies": {
35
+ "typescript": "^5.4.0",
36
+ "@types/node": "^20.0.0",
37
+ "tsx": "^4.0.0",
38
+ "vitest": "^2.0.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/depkeep/ossrisk.git"
46
+ },
47
+ "homepage": "https://github.com/depkeep/ossrisk#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/depkeep/ossrisk/issues"
50
+ }
51
+ }