@4da/mcp-server 4.0.0 → 4.1.1
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.
- package/LICENSE +1 -1
- package/README.md +160 -85
- package/dist/auth.d.ts +36 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +82 -0
- package/dist/auth.js.map +1 -0
- package/dist/db.d.ts +73 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +607 -4
- package/dist/db.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +33 -7
- package/dist/doctor.js.map +1 -1
- package/dist/http-transport.d.ts.map +1 -1
- package/dist/http-transport.js +19 -2
- package/dist/http-transport.js.map +1 -1
- package/dist/index.d.ts +15 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -325
- package/dist/index.js.map +1 -1
- package/dist/live/cache.d.ts +16 -0
- package/dist/live/cache.d.ts.map +1 -0
- package/dist/live/cache.js +61 -0
- package/dist/live/cache.js.map +1 -0
- package/dist/live/hn-fetcher.d.ts +19 -0
- package/dist/live/hn-fetcher.d.ts.map +1 -0
- package/dist/live/hn-fetcher.js +123 -0
- package/dist/live/hn-fetcher.js.map +1 -0
- package/dist/live/index.d.ts +55 -0
- package/dist/live/index.d.ts.map +1 -0
- package/dist/live/index.js +131 -0
- package/dist/live/index.js.map +1 -0
- package/dist/live/osv-scanner.d.ts +19 -0
- package/dist/live/osv-scanner.d.ts.map +1 -0
- package/dist/live/osv-scanner.js +197 -0
- package/dist/live/osv-scanner.js.map +1 -0
- package/dist/live/rate-limiter.d.ts +19 -0
- package/dist/live/rate-limiter.d.ts.map +1 -0
- package/dist/live/rate-limiter.js +52 -0
- package/dist/live/rate-limiter.js.map +1 -0
- package/dist/live/types.d.ts +89 -0
- package/dist/live/types.d.ts.map +1 -0
- package/dist/live/types.js +3 -0
- package/dist/live/types.js.map +1 -0
- package/dist/live/version-resolver.d.ts +11 -0
- package/dist/live/version-resolver.d.ts.map +1 -0
- package/dist/live/version-resolver.js +321 -0
- package/dist/live/version-resolver.js.map +1 -0
- package/dist/live-singleton.d.ts +9 -0
- package/dist/live-singleton.d.ts.map +1 -0
- package/dist/live-singleton.js +14 -0
- package/dist/live-singleton.js.map +1 -0
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +3 -2
- package/dist/llm.js.map +1 -1
- package/dist/output-manager.d.ts.map +1 -1
- package/dist/output-manager.js +1 -0
- package/dist/output-manager.js.map +1 -1
- package/dist/project-scanner.d.ts +27 -0
- package/dist/project-scanner.d.ts.map +1 -0
- package/dist/project-scanner.js +366 -0
- package/dist/project-scanner.js.map +1 -0
- package/dist/schema-registry.d.ts +31 -13
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +164 -32
- package/dist/schema-registry.js.map +1 -1
- package/dist/schemas/agent-feedback-stats.json +14 -0
- package/dist/schemas/preemption-feed.json +18 -0
- package/dist/schemas/record-agent-feedback.json +28 -0
- package/dist/schemas/trust-summary.json +13 -0
- package/dist/schemas/vulnerability-scan.json +26 -0
- package/dist/schemas/what-should-i-know.json +19 -0
- package/dist/setup.d.ts +2 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +38 -4
- package/dist/setup.js.map +1 -1
- package/dist/tool-dispatch.d.ts +33 -0
- package/dist/tool-dispatch.d.ts.map +1 -0
- package/dist/tool-dispatch.js +87 -0
- package/dist/tool-dispatch.js.map +1 -0
- package/dist/tools/agent-feedback-stats.d.ts +30 -0
- package/dist/tools/agent-feedback-stats.d.ts.map +1 -0
- package/dist/tools/agent-feedback-stats.js +41 -0
- package/dist/tools/agent-feedback-stats.js.map +1 -0
- package/dist/tools/agent-memory.d.ts.map +1 -1
- package/dist/tools/agent-memory.js +33 -0
- package/dist/tools/agent-memory.js.map +1 -1
- package/dist/tools/agent-session-brief.d.ts.map +1 -1
- package/dist/tools/agent-session-brief.js +1 -0
- package/dist/tools/agent-session-brief.js.map +1 -1
- package/dist/tools/attention-report.d.ts.map +1 -1
- package/dist/tools/attention-report.js +1 -0
- package/dist/tools/attention-report.js.map +1 -1
- package/dist/tools/autophagy-status.d.ts.map +1 -1
- package/dist/tools/autophagy-status.js.map +1 -1
- package/dist/tools/compound-advantage.d.ts +3 -0
- package/dist/tools/compound-advantage.d.ts.map +1 -1
- package/dist/tools/compound-advantage.js +67 -10
- package/dist/tools/compound-advantage.js.map +1 -1
- package/dist/tools/config-validator.d.ts.map +1 -1
- package/dist/tools/config-validator.js +46 -4
- package/dist/tools/config-validator.js.map +1 -1
- package/dist/tools/context-analysis.d.ts.map +1 -1
- package/dist/tools/context-analysis.js +1 -0
- package/dist/tools/context-analysis.js.map +1 -1
- package/dist/tools/daily-briefing.d.ts.map +1 -1
- package/dist/tools/daily-briefing.js +17 -1
- package/dist/tools/daily-briefing.js.map +1 -1
- package/dist/tools/decision-enforcement.d.ts.map +1 -1
- package/dist/tools/decision-enforcement.js +1 -0
- package/dist/tools/decision-enforcement.js.map +1 -1
- package/dist/tools/decision-memory.d.ts +2 -0
- package/dist/tools/decision-memory.d.ts.map +1 -1
- package/dist/tools/decision-memory.js +80 -0
- package/dist/tools/decision-memory.js.map +1 -1
- package/dist/tools/decision-windows.d.ts.map +1 -1
- package/dist/tools/decision-windows.js.map +1 -1
- package/dist/tools/delegation-score.d.ts.map +1 -1
- package/dist/tools/delegation-score.js +10 -0
- package/dist/tools/delegation-score.js.map +1 -1
- package/dist/tools/developer-dna.d.ts.map +1 -1
- package/dist/tools/developer-dna.js +31 -1
- package/dist/tools/developer-dna.js.map +1 -1
- package/dist/tools/explain-relevance.d.ts.map +1 -1
- package/dist/tools/explain-relevance.js +1 -0
- package/dist/tools/explain-relevance.js.map +1 -1
- package/dist/tools/export-context.d.ts +9 -0
- package/dist/tools/export-context.d.ts.map +1 -1
- package/dist/tools/export-context.js +70 -7
- package/dist/tools/export-context.js.map +1 -1
- package/dist/tools/get-actionable-signals.d.ts.map +1 -1
- package/dist/tools/get-actionable-signals.js +75 -10
- package/dist/tools/get-actionable-signals.js.map +1 -1
- package/dist/tools/get-context.d.ts.map +1 -1
- package/dist/tools/get-context.js +1 -0
- package/dist/tools/get-context.js.map +1 -1
- package/dist/tools/get-relevant-content.d.ts.map +1 -1
- package/dist/tools/get-relevant-content.js +12 -1
- package/dist/tools/get-relevant-content.js.map +1 -1
- package/dist/tools/index.d.ts +16 -16
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +21 -16
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/knowledge-gaps.d.ts +8 -0
- package/dist/tools/knowledge-gaps.d.ts.map +1 -1
- package/dist/tools/knowledge-gaps.js +23 -7
- package/dist/tools/knowledge-gaps.js.map +1 -1
- package/dist/tools/llm-status.d.ts.map +1 -1
- package/dist/tools/llm-status.js +1 -0
- package/dist/tools/llm-status.js.map +1 -1
- package/dist/tools/muse-context.d.ts +195 -0
- package/dist/tools/muse-context.d.ts.map +1 -0
- package/dist/tools/muse-context.js +284 -0
- package/dist/tools/muse-context.js.map +1 -0
- package/dist/tools/preemption-feed.d.ts +55 -0
- package/dist/tools/preemption-feed.d.ts.map +1 -0
- package/dist/tools/preemption-feed.js +124 -0
- package/dist/tools/preemption-feed.js.map +1 -0
- package/dist/tools/project-health.d.ts +12 -0
- package/dist/tools/project-health.d.ts.map +1 -1
- package/dist/tools/project-health.js +62 -11
- package/dist/tools/project-health.js.map +1 -1
- package/dist/tools/record-agent-feedback.d.ts +46 -0
- package/dist/tools/record-agent-feedback.d.ts.map +1 -0
- package/dist/tools/record-agent-feedback.js +71 -0
- package/dist/tools/record-agent-feedback.js.map +1 -0
- package/dist/tools/record-feedback.d.ts.map +1 -1
- package/dist/tools/record-feedback.js +1 -0
- package/dist/tools/record-feedback.js.map +1 -1
- package/dist/tools/reverse-mentions.d.ts.map +1 -1
- package/dist/tools/reverse-mentions.js +31 -13
- package/dist/tools/reverse-mentions.js.map +1 -1
- package/dist/tools/score-autopsy.d.ts.map +1 -1
- package/dist/tools/score-autopsy.js +9 -2
- package/dist/tools/score-autopsy.js.map +1 -1
- package/dist/tools/semantic-shifts.d.ts.map +1 -1
- package/dist/tools/semantic-shifts.js +1 -0
- package/dist/tools/semantic-shifts.js.map +1 -1
- package/dist/tools/signal-chains.d.ts.map +1 -1
- package/dist/tools/signal-chains.js +5 -2
- package/dist/tools/signal-chains.js.map +1 -1
- package/dist/tools/source-health.d.ts.map +1 -1
- package/dist/tools/source-health.js +1 -0
- package/dist/tools/source-health.js.map +1 -1
- package/dist/tools/tech-radar.d.ts.map +1 -1
- package/dist/tools/tech-radar.js +1 -0
- package/dist/tools/tech-radar.js.map +1 -1
- package/dist/tools/topic-connections.d.ts.map +1 -1
- package/dist/tools/topic-connections.js +1 -0
- package/dist/tools/topic-connections.js.map +1 -1
- package/dist/tools/trend-analysis.d.ts.map +1 -1
- package/dist/tools/trend-analysis.js +1 -0
- package/dist/tools/trend-analysis.js.map +1 -1
- package/dist/tools/trust-summary.d.ts +38 -0
- package/dist/tools/trust-summary.d.ts.map +1 -0
- package/dist/tools/trust-summary.js +89 -0
- package/dist/tools/trust-summary.js.map +1 -0
- package/dist/tools/vulnerability-scan.d.ts +89 -0
- package/dist/tools/vulnerability-scan.d.ts.map +1 -0
- package/dist/tools/vulnerability-scan.js +127 -0
- package/dist/tools/vulnerability-scan.js.map +1 -0
- package/dist/tools/what-should-i-know.d.ts +79 -0
- package/dist/tools/what-should-i-know.d.ts.map +1 -0
- package/dist/tools/what-should-i-know.js +380 -0
- package/dist/tools/what-should-i-know.js.map +1 -0
- package/dist/types.d.ts +112 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +35 -18
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-License-Identifier: FSL-1.1-Apache-2.0
|
|
2
|
+
export class LiveCache {
|
|
3
|
+
db;
|
|
4
|
+
constructor(db) {
|
|
5
|
+
this.db = db;
|
|
6
|
+
this.ensureTable();
|
|
7
|
+
this.purgeExpired();
|
|
8
|
+
}
|
|
9
|
+
ensureTable() {
|
|
10
|
+
this.db.exec(`
|
|
11
|
+
CREATE TABLE IF NOT EXISTS live_cache (
|
|
12
|
+
cache_key TEXT PRIMARY KEY,
|
|
13
|
+
data TEXT NOT NULL,
|
|
14
|
+
source TEXT NOT NULL,
|
|
15
|
+
fetched_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
16
|
+
expires_at TEXT NOT NULL
|
|
17
|
+
);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_live_cache_expires ON live_cache(expires_at);
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_live_cache_source ON live_cache(source);
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
get(key) {
|
|
23
|
+
const row = this.db.prepare("SELECT data FROM live_cache WHERE cache_key = ? AND expires_at > datetime('now')").get(key);
|
|
24
|
+
if (!row)
|
|
25
|
+
return null;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(row.data);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
getStale(key) {
|
|
34
|
+
const row = this.db.prepare("SELECT data, fetched_at FROM live_cache WHERE cache_key = ?").get(key);
|
|
35
|
+
if (!row)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
return { data: JSON.parse(row.data), fetchedAt: row.fetched_at };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
set(key, data, source, ttlSeconds) {
|
|
45
|
+
this.db.prepare(`
|
|
46
|
+
INSERT OR REPLACE INTO live_cache (cache_key, data, source, fetched_at, expires_at)
|
|
47
|
+
VALUES (?, ?, ?, datetime('now'), datetime('now', '+' || ? || ' seconds'))
|
|
48
|
+
`).run(key, JSON.stringify(data), source, ttlSeconds);
|
|
49
|
+
}
|
|
50
|
+
purgeExpired() {
|
|
51
|
+
const result = this.db.prepare("DELETE FROM live_cache WHERE expires_at <= datetime('now')").run();
|
|
52
|
+
return result.changes;
|
|
53
|
+
}
|
|
54
|
+
invalidateSource(source) {
|
|
55
|
+
this.db.prepare("DELETE FROM live_cache WHERE source = ?").run(source);
|
|
56
|
+
}
|
|
57
|
+
invalidateAll() {
|
|
58
|
+
this.db.prepare("DELETE FROM live_cache").run();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/live/cache.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAY9C,MAAM,OAAO,SAAS;IACZ,EAAE,CAAoB;IAE9B,YAAY,EAAqB;QAC/B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;KAUZ,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAI,GAAW;QAChB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACzB,kFAAkF,CACnF,CAAC,GAAG,CAAC,GAAG,CAAiC,CAAC;QAE3C,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAM,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,QAAQ,CAAI,GAAW;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACzB,6DAA6D,CAC9D,CAAC,GAAG,CAAC,GAAG,CAAqD,CAAC;QAE/D,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAM,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,IAAa,EAAE,MAAc,EAAE,UAAkB;QAChE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;KAGf,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IACxD,CAAC;IAED,YAAY;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC5B,4DAA4D,CAC7D,CAAC,GAAG,EAAE,CAAC;QACR,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,gBAAgB,CAAC,MAAc;QAC7B,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzE,CAAC;IAED,aAAa;QACX,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,GAAG,EAAE,CAAC;IAClD,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hacker News Headline Fetcher
|
|
3
|
+
*
|
|
4
|
+
* Queries HN Algolia API for headlines relevant to the user's tech stack.
|
|
5
|
+
* Free, no auth. Generates search queries from detected technologies.
|
|
6
|
+
*
|
|
7
|
+
* Privacy: sends generic tech keywords (e.g. "react", "rust") — not personal data.
|
|
8
|
+
*/
|
|
9
|
+
import type { LiveCache } from "./cache.js";
|
|
10
|
+
import type { RateLimiter } from "./rate-limiter.js";
|
|
11
|
+
import type { LiveHeadline } from "./types.js";
|
|
12
|
+
export declare class HNFetcher {
|
|
13
|
+
private cache;
|
|
14
|
+
private rateLimiter;
|
|
15
|
+
constructor(cache: LiveCache, rateLimiter: RateLimiter);
|
|
16
|
+
fetch(techStack: string[]): Promise<LiveHeadline[]>;
|
|
17
|
+
private searchHN;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=hn-fetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hn-fetcher.d.ts","sourceRoot":"","sources":["../../src/live/hn-fetcher.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAuB/C,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,WAAW,CAAc;gBAErB,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW;IAKhD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YAmC3C,QAAQ;CAqBvB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// SPDX-License-Identifier: FSL-1.1-Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Hacker News Headline Fetcher
|
|
4
|
+
*
|
|
5
|
+
* Queries HN Algolia API for headlines relevant to the user's tech stack.
|
|
6
|
+
* Free, no auth. Generates search queries from detected technologies.
|
|
7
|
+
*
|
|
8
|
+
* Privacy: sends generic tech keywords (e.g. "react", "rust") — not personal data.
|
|
9
|
+
*/
|
|
10
|
+
const HN_ALGOLIA_URL = "https://hn.algolia.com/api/v1/search";
|
|
11
|
+
const HN_TIMEOUT_MS = 8_000;
|
|
12
|
+
const HN_CACHE_TTL = 1800; // 30 minutes
|
|
13
|
+
const MAX_QUERIES = 5;
|
|
14
|
+
const RESULTS_PER_QUERY = 10;
|
|
15
|
+
const MIN_POINTS = 10;
|
|
16
|
+
export class HNFetcher {
|
|
17
|
+
cache;
|
|
18
|
+
rateLimiter;
|
|
19
|
+
constructor(cache, rateLimiter) {
|
|
20
|
+
this.cache = cache;
|
|
21
|
+
this.rateLimiter = rateLimiter;
|
|
22
|
+
}
|
|
23
|
+
async fetch(techStack) {
|
|
24
|
+
const cacheKey = `hn:${techStack.slice(0, MAX_QUERIES).sort().join(",")}`;
|
|
25
|
+
const cached = this.cache.get(cacheKey);
|
|
26
|
+
if (cached !== null)
|
|
27
|
+
return cached;
|
|
28
|
+
if (!this.rateLimiter.canProceed("hn")) {
|
|
29
|
+
const stale = this.cache.getStale(cacheKey);
|
|
30
|
+
return stale?.data || [];
|
|
31
|
+
}
|
|
32
|
+
const queries = buildQueries(techStack);
|
|
33
|
+
const allHits = [];
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
for (const query of queries) {
|
|
36
|
+
if (!this.rateLimiter.consume("hn"))
|
|
37
|
+
break;
|
|
38
|
+
try {
|
|
39
|
+
const hits = await this.searchHN(query);
|
|
40
|
+
for (const hit of hits) {
|
|
41
|
+
if (!seen.has(hit.objectID)) {
|
|
42
|
+
seen.add(hit.objectID);
|
|
43
|
+
allHits.push(hit);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Individual query failure — continue with others
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const headlines = scoreAndMap(allHits, techStack);
|
|
52
|
+
this.cache.set(cacheKey, headlines, "hn", HN_CACHE_TTL);
|
|
53
|
+
return headlines;
|
|
54
|
+
}
|
|
55
|
+
async searchHN(query) {
|
|
56
|
+
const params = new URLSearchParams({
|
|
57
|
+
query,
|
|
58
|
+
tags: "story",
|
|
59
|
+
numericFilters: `points>${MIN_POINTS}`,
|
|
60
|
+
hitsPerPage: String(RESULTS_PER_QUERY),
|
|
61
|
+
});
|
|
62
|
+
const url = `${HN_ALGOLIA_URL}?${params}`;
|
|
63
|
+
const controller = new AbortController();
|
|
64
|
+
const timeout = setTimeout(() => controller.abort(), HN_TIMEOUT_MS);
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
67
|
+
if (!response.ok)
|
|
68
|
+
return [];
|
|
69
|
+
const data = (await response.json());
|
|
70
|
+
return data.hits || [];
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
clearTimeout(timeout);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function buildQueries(techStack) {
|
|
78
|
+
// Take top technologies, prioritize frameworks over languages
|
|
79
|
+
const prioritized = [...techStack]
|
|
80
|
+
.filter((t) => t.length >= 2 && !SKIP_TERMS.has(t))
|
|
81
|
+
.slice(0, MAX_QUERIES);
|
|
82
|
+
return prioritized;
|
|
83
|
+
}
|
|
84
|
+
function wordBoundaryMatch(text, term) {
|
|
85
|
+
const idx = text.indexOf(term);
|
|
86
|
+
if (idx === -1)
|
|
87
|
+
return false;
|
|
88
|
+
const before = idx === 0 || !/[a-zA-Z0-9]/.test(text[idx - 1]);
|
|
89
|
+
const after = idx + term.length >= text.length || !/[a-zA-Z0-9]/.test(text[idx + term.length]);
|
|
90
|
+
return before && after;
|
|
91
|
+
}
|
|
92
|
+
function scoreAndMap(hits, techStack) {
|
|
93
|
+
const techLower = techStack.map((t) => t.toLowerCase());
|
|
94
|
+
return hits
|
|
95
|
+
.map((hit) => {
|
|
96
|
+
const titleLower = hit.title.toLowerCase();
|
|
97
|
+
const matchedTech = techLower.filter((t) => wordBoundaryMatch(titleLower, t));
|
|
98
|
+
const techBoost = Math.min(matchedTech.length * 0.2, 0.4);
|
|
99
|
+
const pointsBoost = Math.min(hit.points / 500, 0.3);
|
|
100
|
+
const commentsBoost = Math.min(hit.num_comments / 200, 0.2);
|
|
101
|
+
const score = Math.min(0.1 + techBoost + pointsBoost + commentsBoost, 1.0);
|
|
102
|
+
return {
|
|
103
|
+
id: hit.objectID,
|
|
104
|
+
title: hit.title,
|
|
105
|
+
url: hit.url,
|
|
106
|
+
source: "hacker_news",
|
|
107
|
+
points: hit.points,
|
|
108
|
+
comments: hit.num_comments,
|
|
109
|
+
published: hit.created_at,
|
|
110
|
+
relevanceScore: Math.round(score * 100) / 100,
|
|
111
|
+
relevanceReason: matchedTech.length > 0
|
|
112
|
+
? `Matches your stack: ${matchedTech.join(", ")}`
|
|
113
|
+
: `Trending (${hit.points} points)`,
|
|
114
|
+
};
|
|
115
|
+
})
|
|
116
|
+
.sort((a, b) => b.relevanceScore - a.relevanceScore)
|
|
117
|
+
.slice(0, 15);
|
|
118
|
+
}
|
|
119
|
+
const SKIP_TERMS = new Set([
|
|
120
|
+
"javascript", "typescript", // too generic, floods results
|
|
121
|
+
"docker", // too broad
|
|
122
|
+
]);
|
|
123
|
+
//# sourceMappingURL=hn-fetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hn-fetcher.js","sourceRoot":"","sources":["../../src/live/hn-fetcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Intelligence Coordinator
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates vulnerability scanning and headline fetching.
|
|
5
|
+
* Manages cache lifecycle and offline fallback.
|
|
6
|
+
*
|
|
7
|
+
* Privacy: Only sends package names/versions (public) and tech keywords (generic).
|
|
8
|
+
* Set FOURDA_OFFLINE=true to disable all network calls.
|
|
9
|
+
*/
|
|
10
|
+
import type Database from "better-sqlite3";
|
|
11
|
+
import type { ResolvedDependency, VulnerabilityScanResult, LiveHeadline, LiveIntelligenceStatus } from "./types.js";
|
|
12
|
+
export type { VulnerabilityScanResult, VulnerabilityEntry, LiveHeadline, LiveIntelligenceStatus } from "./types.js";
|
|
13
|
+
export declare class LiveIntelligence {
|
|
14
|
+
private cache;
|
|
15
|
+
private rateLimiter;
|
|
16
|
+
private osvScanner;
|
|
17
|
+
private hnFetcher;
|
|
18
|
+
private enabled;
|
|
19
|
+
private lastVulnScan;
|
|
20
|
+
private lastHeadlines;
|
|
21
|
+
private resolvedDeps;
|
|
22
|
+
private initialized;
|
|
23
|
+
constructor(db: Database.Database);
|
|
24
|
+
/**
|
|
25
|
+
* Initialize with project data. Call once after project scan.
|
|
26
|
+
*/
|
|
27
|
+
initFromProject(projectPath: string, deps: string[], devDeps: string[], language: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Run vulnerability scan (returns cached if fresh, fetches otherwise).
|
|
30
|
+
*/
|
|
31
|
+
scanVulnerabilities(projectPath: string, options?: {
|
|
32
|
+
includeDev?: boolean;
|
|
33
|
+
forceRefresh?: boolean;
|
|
34
|
+
}): Promise<VulnerabilityScanResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Fetch relevant headlines for the user's tech stack.
|
|
37
|
+
*/
|
|
38
|
+
fetchHeadlines(techStack: string[]): Promise<LiveHeadline[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Get last vulnerability scan result (from cache/memory, no network).
|
|
41
|
+
*/
|
|
42
|
+
getVulnerabilities(): VulnerabilityScanResult | null;
|
|
43
|
+
/**
|
|
44
|
+
* Get last headlines (from cache/memory, no network).
|
|
45
|
+
*/
|
|
46
|
+
getHeadlines(): LiveHeadline[];
|
|
47
|
+
/**
|
|
48
|
+
* Get resolved dependencies with versions.
|
|
49
|
+
*/
|
|
50
|
+
getResolvedDeps(): ResolvedDependency[];
|
|
51
|
+
isEnabled(): boolean;
|
|
52
|
+
isInitialized(): boolean;
|
|
53
|
+
getStatus(): LiveIntelligenceStatus;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/live/index.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAM3C,OAAO,KAAK,EACV,kBAAkB,EAClB,uBAAuB,EACvB,YAAY,EACZ,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEpH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAU;IAEzB,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,WAAW,CAAS;gBAEhB,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAQjC;;OAEG;IACH,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,GACf,IAAI;IAKP;;OAEG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,GACzD,OAAO,CAAC,uBAAuB,CAAC;IA2BnC;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAWlE;;OAEG;IACH,kBAAkB,IAAI,uBAAuB,GAAG,IAAI;IAIpD;;OAEG;IACH,YAAY,IAAI,YAAY,EAAE;IAI9B;;OAEG;IACH,eAAe,IAAI,kBAAkB,EAAE;IAIvC,SAAS,IAAI,OAAO;IAIpB,aAAa,IAAI,OAAO;IAIxB,SAAS,IAAI,sBAAsB;CAUpC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// SPDX-License-Identifier: FSL-1.1-Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Live Intelligence Coordinator
|
|
4
|
+
*
|
|
5
|
+
* Orchestrates vulnerability scanning and headline fetching.
|
|
6
|
+
* Manages cache lifecycle and offline fallback.
|
|
7
|
+
*
|
|
8
|
+
* Privacy: Only sends package names/versions (public) and tech keywords (generic).
|
|
9
|
+
* Set FOURDA_OFFLINE=true to disable all network calls.
|
|
10
|
+
*/
|
|
11
|
+
import { LiveCache } from "./cache.js";
|
|
12
|
+
import { RateLimiter, DEFAULT_RATE_LIMITS } from "./rate-limiter.js";
|
|
13
|
+
import { OsvScanner } from "./osv-scanner.js";
|
|
14
|
+
import { HNFetcher } from "./hn-fetcher.js";
|
|
15
|
+
import { resolveVersions } from "./version-resolver.js";
|
|
16
|
+
export class LiveIntelligence {
|
|
17
|
+
cache;
|
|
18
|
+
rateLimiter;
|
|
19
|
+
osvScanner;
|
|
20
|
+
hnFetcher;
|
|
21
|
+
enabled;
|
|
22
|
+
lastVulnScan = null;
|
|
23
|
+
lastHeadlines = [];
|
|
24
|
+
resolvedDeps = [];
|
|
25
|
+
initialized = false;
|
|
26
|
+
constructor(db) {
|
|
27
|
+
this.enabled = process.env.FOURDA_OFFLINE !== "true";
|
|
28
|
+
this.cache = new LiveCache(db);
|
|
29
|
+
this.rateLimiter = new RateLimiter(DEFAULT_RATE_LIMITS);
|
|
30
|
+
this.osvScanner = new OsvScanner(this.cache, this.rateLimiter);
|
|
31
|
+
this.hnFetcher = new HNFetcher(this.cache, this.rateLimiter);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Initialize with project data. Call once after project scan.
|
|
35
|
+
*/
|
|
36
|
+
initFromProject(projectPath, deps, devDeps, language) {
|
|
37
|
+
this.resolvedDeps = resolveVersions(projectPath, deps, devDeps, language);
|
|
38
|
+
this.initialized = true;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Run vulnerability scan (returns cached if fresh, fetches otherwise).
|
|
42
|
+
*/
|
|
43
|
+
async scanVulnerabilities(projectPath, options) {
|
|
44
|
+
if (!this.enabled) {
|
|
45
|
+
return emptyVulnResult(projectPath, true);
|
|
46
|
+
}
|
|
47
|
+
const deps = options?.includeDev
|
|
48
|
+
? this.resolvedDeps
|
|
49
|
+
: this.resolvedDeps.filter((d) => !d.isDev);
|
|
50
|
+
if (deps.length === 0) {
|
|
51
|
+
return emptyVulnResult(projectPath, false);
|
|
52
|
+
}
|
|
53
|
+
if (options?.forceRefresh) {
|
|
54
|
+
this.cache.invalidateSource("osv");
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
this.lastVulnScan = await this.osvScanner.scan(deps, projectPath);
|
|
58
|
+
return this.lastVulnScan;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Network failure — return last known or empty
|
|
62
|
+
if (this.lastVulnScan)
|
|
63
|
+
return { ...this.lastVulnScan, offline: true, cached: true };
|
|
64
|
+
return emptyVulnResult(projectPath, true);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fetch relevant headlines for the user's tech stack.
|
|
69
|
+
*/
|
|
70
|
+
async fetchHeadlines(techStack) {
|
|
71
|
+
if (!this.enabled)
|
|
72
|
+
return [];
|
|
73
|
+
try {
|
|
74
|
+
this.lastHeadlines = await this.hnFetcher.fetch(techStack);
|
|
75
|
+
return this.lastHeadlines;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return this.lastHeadlines; // Return last known
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get last vulnerability scan result (from cache/memory, no network).
|
|
83
|
+
*/
|
|
84
|
+
getVulnerabilities() {
|
|
85
|
+
return this.lastVulnScan;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get last headlines (from cache/memory, no network).
|
|
89
|
+
*/
|
|
90
|
+
getHeadlines() {
|
|
91
|
+
return this.lastHeadlines;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get resolved dependencies with versions.
|
|
95
|
+
*/
|
|
96
|
+
getResolvedDeps() {
|
|
97
|
+
return this.resolvedDeps;
|
|
98
|
+
}
|
|
99
|
+
isEnabled() {
|
|
100
|
+
return this.enabled;
|
|
101
|
+
}
|
|
102
|
+
isInitialized() {
|
|
103
|
+
return this.initialized;
|
|
104
|
+
}
|
|
105
|
+
getStatus() {
|
|
106
|
+
return {
|
|
107
|
+
enabled: this.enabled,
|
|
108
|
+
offline: !this.enabled,
|
|
109
|
+
lastOsvRefresh: this.lastVulnScan?.scannedAt || null,
|
|
110
|
+
lastHnRefresh: this.lastHeadlines.length > 0 ? new Date().toISOString() : null,
|
|
111
|
+
cachedVulnCount: this.lastVulnScan?.totalVulnerable || 0,
|
|
112
|
+
cachedHeadlineCount: this.lastHeadlines.length,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function emptyVulnResult(projectPath, offline) {
|
|
117
|
+
return {
|
|
118
|
+
scannedAt: new Date().toISOString(),
|
|
119
|
+
projectPath,
|
|
120
|
+
ecosystemsScanned: [],
|
|
121
|
+
totalScanned: 0,
|
|
122
|
+
totalVulnerable: 0,
|
|
123
|
+
bySeverity: { critical: 0, high: 0, medium: 0, low: 0, unknown: 0 },
|
|
124
|
+
vulnerabilities: [],
|
|
125
|
+
cleanCount: 0,
|
|
126
|
+
scanDurationMs: 0,
|
|
127
|
+
cached: false,
|
|
128
|
+
offline,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/live/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OSV.dev Vulnerability Scanner
|
|
3
|
+
*
|
|
4
|
+
* Batch-queries the Open Source Vulnerability database for known CVEs
|
|
5
|
+
* affecting the user's exact dependency versions. Free, no auth required.
|
|
6
|
+
*
|
|
7
|
+
* Privacy: sends only package names + versions (public manifest data).
|
|
8
|
+
*/
|
|
9
|
+
import type { LiveCache } from "./cache.js";
|
|
10
|
+
import type { RateLimiter } from "./rate-limiter.js";
|
|
11
|
+
import type { ResolvedDependency, VulnerabilityScanResult } from "./types.js";
|
|
12
|
+
export declare class OsvScanner {
|
|
13
|
+
private cache;
|
|
14
|
+
private rateLimiter;
|
|
15
|
+
constructor(cache: LiveCache, rateLimiter: RateLimiter);
|
|
16
|
+
scan(deps: ResolvedDependency[], projectPath: string): Promise<VulnerabilityScanResult>;
|
|
17
|
+
private batchQuery;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=osv-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osv-scanner.d.ts","sourceRoot":"","sources":["../../src/live/osv-scanner.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EACV,kBAAkB,EAGlB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAgBpB,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,WAAW,CAAc;gBAErB,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW;IAKhD,IAAI,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;YAiE/E,UAAU;CAuCzB"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// SPDX-License-Identifier: FSL-1.1-Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* OSV.dev Vulnerability Scanner
|
|
4
|
+
*
|
|
5
|
+
* Batch-queries the Open Source Vulnerability database for known CVEs
|
|
6
|
+
* affecting the user's exact dependency versions. Free, no auth required.
|
|
7
|
+
*
|
|
8
|
+
* Privacy: sends only package names + versions (public manifest data).
|
|
9
|
+
*/
|
|
10
|
+
const OSV_BATCH_URL = "https://api.osv.dev/v1/querybatch";
|
|
11
|
+
const OSV_TIMEOUT_MS = 15_000;
|
|
12
|
+
const OSV_CACHE_TTL = 3600; // 1 hour
|
|
13
|
+
const MAX_BATCH_SIZE = 200;
|
|
14
|
+
export class OsvScanner {
|
|
15
|
+
cache;
|
|
16
|
+
rateLimiter;
|
|
17
|
+
constructor(cache, rateLimiter) {
|
|
18
|
+
this.cache = cache;
|
|
19
|
+
this.rateLimiter = rateLimiter;
|
|
20
|
+
}
|
|
21
|
+
async scan(deps, projectPath) {
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
const scannable = deps.filter((d) => d.version !== null).slice(0, MAX_BATCH_SIZE);
|
|
24
|
+
const ecosystems = [...new Set(scannable.map((d) => d.ecosystem))];
|
|
25
|
+
// Check cache for each dep individually
|
|
26
|
+
const uncached = [];
|
|
27
|
+
const cachedVulns = [];
|
|
28
|
+
for (const dep of scannable) {
|
|
29
|
+
const cacheKey = `osv:${dep.ecosystem}:${dep.name}:${dep.version}`;
|
|
30
|
+
const cached = this.cache.get(cacheKey);
|
|
31
|
+
if (cached !== null) {
|
|
32
|
+
cachedVulns.push(...cached);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
uncached.push(dep);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Fetch uncached from OSV
|
|
39
|
+
let fetchedVulns = [];
|
|
40
|
+
let offline = false;
|
|
41
|
+
if (uncached.length > 0) {
|
|
42
|
+
if (!this.rateLimiter.canProceed("osv")) {
|
|
43
|
+
offline = true;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
try {
|
|
47
|
+
fetchedVulns = await this.batchQuery(uncached);
|
|
48
|
+
this.rateLimiter.consume("osv");
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
offline = true;
|
|
52
|
+
// Try stale cache for uncached deps
|
|
53
|
+
for (const dep of uncached) {
|
|
54
|
+
const cacheKey = `osv:${dep.ecosystem}:${dep.name}:${dep.version}`;
|
|
55
|
+
const stale = this.cache.getStale(cacheKey);
|
|
56
|
+
if (stale)
|
|
57
|
+
cachedVulns.push(...stale.data);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const allVulns = [...cachedVulns, ...fetchedVulns];
|
|
63
|
+
const vulnerablePackages = new Set(allVulns.map((v) => v.package));
|
|
64
|
+
const bySeverity = { critical: 0, high: 0, medium: 0, low: 0, unknown: 0 };
|
|
65
|
+
for (const v of allVulns) {
|
|
66
|
+
bySeverity[v.severity]++;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
scannedAt: new Date().toISOString(),
|
|
70
|
+
projectPath,
|
|
71
|
+
ecosystemsScanned: ecosystems,
|
|
72
|
+
totalScanned: scannable.length,
|
|
73
|
+
totalVulnerable: vulnerablePackages.size,
|
|
74
|
+
bySeverity,
|
|
75
|
+
vulnerabilities: allVulns,
|
|
76
|
+
cleanCount: scannable.length - vulnerablePackages.size,
|
|
77
|
+
scanDurationMs: Date.now() - start,
|
|
78
|
+
cached: uncached.length === 0,
|
|
79
|
+
offline,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async batchQuery(deps) {
|
|
83
|
+
const queries = deps.map((d) => ({
|
|
84
|
+
package: { name: d.name, ecosystem: d.ecosystem },
|
|
85
|
+
...(d.version ? { version: d.version } : {}),
|
|
86
|
+
}));
|
|
87
|
+
const response = await fetchWithTimeout(OSV_BATCH_URL, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: { "Content-Type": "application/json" },
|
|
90
|
+
body: JSON.stringify({ queries }),
|
|
91
|
+
}, OSV_TIMEOUT_MS);
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error(`OSV API error: ${response.status}`);
|
|
94
|
+
}
|
|
95
|
+
const data = (await response.json());
|
|
96
|
+
const results = [];
|
|
97
|
+
for (let i = 0; i < data.results.length; i++) {
|
|
98
|
+
const dep = deps[i];
|
|
99
|
+
const osvResult = data.results[i];
|
|
100
|
+
const depVulns = [];
|
|
101
|
+
if (osvResult.vulns && osvResult.vulns.length > 0) {
|
|
102
|
+
for (const vuln of osvResult.vulns) {
|
|
103
|
+
depVulns.push(mapVulnerability(vuln, dep));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Cache per-dep (even empty results to avoid re-fetching clean deps)
|
|
107
|
+
const cacheKey = `osv:${dep.ecosystem}:${dep.name}:${dep.version}`;
|
|
108
|
+
this.cache.set(cacheKey, depVulns, "osv", OSV_CACHE_TTL);
|
|
109
|
+
results.push(...depVulns);
|
|
110
|
+
}
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function mapVulnerability(vuln, dep) {
|
|
115
|
+
const cvssScore = extractCvssScore(vuln.severity);
|
|
116
|
+
const severity = cvssScore !== null ? cvssToSeverity(cvssScore) : guessSeverityFromId(vuln.id);
|
|
117
|
+
const fixedVersion = extractFixedVersion(vuln.affected, dep.name, dep.ecosystem);
|
|
118
|
+
return {
|
|
119
|
+
package: dep.name,
|
|
120
|
+
currentVersion: dep.version || "unknown",
|
|
121
|
+
ecosystem: dep.ecosystem,
|
|
122
|
+
isDev: dep.isDev,
|
|
123
|
+
vulnId: vuln.id,
|
|
124
|
+
aliases: vuln.aliases || [],
|
|
125
|
+
severity,
|
|
126
|
+
cvssScore,
|
|
127
|
+
summary: vuln.summary || vuln.details?.slice(0, 200) || vuln.id,
|
|
128
|
+
fixedVersion,
|
|
129
|
+
published: vuln.published || vuln.modified || "",
|
|
130
|
+
references: (vuln.references || [])
|
|
131
|
+
.filter((r) => r.type === "ADVISORY" || r.type === "WEB")
|
|
132
|
+
.map((r) => r.url)
|
|
133
|
+
.slice(0, 3),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function extractCvssScore(severity) {
|
|
137
|
+
if (!severity || severity.length === 0)
|
|
138
|
+
return null;
|
|
139
|
+
for (const s of severity) {
|
|
140
|
+
if (s.type === "CVSS_V3" || s.type === "CVSS_V4") {
|
|
141
|
+
// CVSS vector string: "CVSS:3.1/AV:N/AC:L/..." — extract base score
|
|
142
|
+
const scoreMatch = s.score.match(/(\d+\.?\d*)/);
|
|
143
|
+
if (scoreMatch)
|
|
144
|
+
return parseFloat(scoreMatch[1]);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Some entries have just a numeric score
|
|
148
|
+
for (const s of severity) {
|
|
149
|
+
const num = parseFloat(s.score);
|
|
150
|
+
if (!isNaN(num) && num >= 0 && num <= 10)
|
|
151
|
+
return num;
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function cvssToSeverity(score) {
|
|
156
|
+
if (score >= 9.0)
|
|
157
|
+
return "critical";
|
|
158
|
+
if (score >= 7.0)
|
|
159
|
+
return "high";
|
|
160
|
+
if (score >= 4.0)
|
|
161
|
+
return "medium";
|
|
162
|
+
if (score > 0)
|
|
163
|
+
return "low";
|
|
164
|
+
return "unknown";
|
|
165
|
+
}
|
|
166
|
+
function guessSeverityFromId(id) {
|
|
167
|
+
// GHSA severity is sometimes encoded in the advisory
|
|
168
|
+
if (id.startsWith("GHSA-"))
|
|
169
|
+
return "medium"; // conservative default
|
|
170
|
+
return "unknown";
|
|
171
|
+
}
|
|
172
|
+
function extractFixedVersion(affected, packageName, ecosystem) {
|
|
173
|
+
if (!affected)
|
|
174
|
+
return null;
|
|
175
|
+
for (const a of affected) {
|
|
176
|
+
if (a.package.name === packageName && a.package.ecosystem === ecosystem) {
|
|
177
|
+
for (const range of a.ranges || []) {
|
|
178
|
+
for (const event of range.events || []) {
|
|
179
|
+
if (event.fixed)
|
|
180
|
+
return event.fixed;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
async function fetchWithTimeout(url, options, timeoutMs) {
|
|
188
|
+
const controller = new AbortController();
|
|
189
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
190
|
+
try {
|
|
191
|
+
return await fetch(url, { ...options, signal: controller.signal });
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
clearTimeout(timeout);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=osv-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osv-scanner.js","sourceRoot":"","sources":["../../src/live/osv-scanner.ts"],"names":[],"mappings":"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"}
|