@telora/daemon 0.15.36 → 0.15.40
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/build-info.json +2 -2
- package/dist/assembly-resolvers.d.ts +1 -1
- package/dist/assembly-resolvers.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/delivery-lifecycle.d.ts +3 -0
- package/dist/delivery-lifecycle.d.ts.map +1 -1
- package/dist/delivery-lifecycle.js +13 -1
- package/dist/delivery-lifecycle.js.map +1 -1
- package/dist/drift-eval-loop.d.ts +63 -0
- package/dist/drift-eval-loop.d.ts.map +1 -0
- package/dist/drift-eval-loop.js +215 -0
- package/dist/drift-eval-loop.js.map +1 -0
- package/dist/drift-evaluator.d.ts +51 -0
- package/dist/drift-evaluator.d.ts.map +1 -0
- package/dist/drift-evaluator.js +62 -0
- package/dist/drift-evaluator.js.map +1 -0
- package/dist/feeds/ghsa.d.ts +88 -0
- package/dist/feeds/ghsa.d.ts.map +1 -0
- package/dist/feeds/ghsa.js +219 -0
- package/dist/feeds/ghsa.js.map +1 -0
- package/dist/feeds/local.d.ts +55 -0
- package/dist/feeds/local.d.ts.map +1 -0
- package/dist/feeds/local.js +196 -0
- package/dist/feeds/local.js.map +1 -0
- package/dist/feeds/osv.d.ts +89 -0
- package/dist/feeds/osv.d.ts.map +1 -0
- package/dist/feeds/osv.js +266 -0
- package/dist/feeds/osv.js.map +1 -0
- package/dist/focus-completion-event.d.ts +2 -0
- package/dist/focus-completion-event.d.ts.map +1 -1
- package/dist/focus-completion-event.js +51 -13
- package/dist/focus-completion-event.js.map +1 -1
- package/dist/focus-completion.d.ts +1 -1
- package/dist/focus-completion.js +7 -7
- package/dist/focus-completion.js.map +1 -1
- package/dist/focus-engine.d.ts.map +1 -1
- package/dist/focus-engine.js +71 -0
- package/dist/focus-engine.js.map +1 -1
- package/dist/focus-executor.d.ts +53 -0
- package/dist/focus-executor.d.ts.map +1 -1
- package/dist/focus-executor.js +41 -26
- package/dist/focus-executor.js.map +1 -1
- package/dist/focus-loop.d.ts +1 -1
- package/dist/focus-loop.js +2 -2
- package/dist/focus-loop.js.map +1 -1
- package/dist/focus-phase.js +1 -1
- package/dist/focus-phase.js.map +1 -1
- package/dist/focus-prompt-builder.d.ts +1 -1
- package/dist/focus-prompt-builder.js +1 -1
- package/dist/listener-auto-advance.d.ts +3 -3
- package/dist/listener-auto-advance.js +10 -10
- package/dist/listener-auto-advance.js.map +1 -1
- package/dist/listener.js +1 -1
- package/dist/listener.js.map +1 -1
- package/dist/queries/deliveries.d.ts +20 -1
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +8 -1
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/drift.d.ts +50 -0
- package/dist/queries/drift.d.ts.map +1 -0
- package/dist/queries/drift.js +28 -0
- package/dist/queries/drift.js.map +1 -0
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +1 -0
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/index.d.ts +2 -0
- package/dist/queries/index.d.ts.map +1 -1
- package/dist/queries/index.js +1 -0
- package/dist/queries/index.js.map +1 -1
- package/dist/queries/schemas.d.ts +2 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +1 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/scanners/deps.d.ts +101 -0
- package/dist/scanners/deps.d.ts.map +1 -0
- package/dist/scanners/deps.js +242 -0
- package/dist/scanners/deps.js.map +1 -0
- package/dist/scanners/signatures.d.ts +44 -0
- package/dist/scanners/signatures.d.ts.map +1 -0
- package/dist/scanners/signatures.js +140 -0
- package/dist/scanners/signatures.js.map +1 -0
- package/dist/scanners/workflow.d.ts +34 -0
- package/dist/scanners/workflow.d.ts.map +1 -0
- package/dist/scanners/workflow.js +239 -0
- package/dist/scanners/workflow.js.map +1 -0
- package/dist/security-auto-inject.d.ts +114 -0
- package/dist/security-auto-inject.d.ts.map +1 -0
- package/dist/security-auto-inject.js +148 -0
- package/dist/security-auto-inject.js.map +1 -0
- package/dist/security-rescan-resolution.d.ts +84 -0
- package/dist/security-rescan-resolution.d.ts.map +1 -0
- package/dist/security-rescan-resolution.js +114 -0
- package/dist/security-rescan-resolution.js.map +1 -0
- package/dist/security-scan-engine.d.ts +96 -0
- package/dist/security-scan-engine.d.ts.map +1 -0
- package/dist/security-scan-engine.js +189 -0
- package/dist/security-scan-engine.js.map +1 -0
- package/dist/stage-classifier.d.ts +2 -2
- package/dist/stage-classifier.d.ts.map +1 -1
- package/dist/stage-classifier.js +7 -7
- package/dist/stage-classifier.js.map +1 -1
- package/dist/state-cascade.d.ts +1 -1
- package/dist/state-cascade.js +1 -1
- package/dist/team-prompt-base.d.ts.map +1 -1
- package/dist/team-prompt-base.js +15 -0
- package/dist/team-prompt-base.js.map +1 -1
- package/dist/types/focus.d.ts +6 -0
- package/dist/types/focus.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Security Advisory (GHSA) feed adapter.
|
|
3
|
+
*
|
|
4
|
+
* Cross-references installed dependencies against GitHub's security advisory
|
|
5
|
+
* database. Used alongside OSV by the deps scanner. The endpoint
|
|
6
|
+
* (`GET /advisories`) does not batch packages, so this module issues one
|
|
7
|
+
* request per `(ecosystem, name)` and merges/dedupes by `ghsa_id`.
|
|
8
|
+
*
|
|
9
|
+
* Resilience contract: this function never throws. Any fetch error or
|
|
10
|
+
* non-200 response is collected as a warning string; callers always receive
|
|
11
|
+
* a `GhsaQueryResult` with `advisories` and `warnings` arrays. An empty
|
|
12
|
+
* advisories list with a populated warnings list is the GHSA-down signal.
|
|
13
|
+
*
|
|
14
|
+
* @module feeds/ghsa
|
|
15
|
+
*/
|
|
16
|
+
export interface GhsaAdvisory {
|
|
17
|
+
ghsa_id: string;
|
|
18
|
+
cve_id: string | null;
|
|
19
|
+
summary: string;
|
|
20
|
+
description: string;
|
|
21
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
22
|
+
cvss?: {
|
|
23
|
+
score: number;
|
|
24
|
+
vector_string: string;
|
|
25
|
+
};
|
|
26
|
+
identifiers: Array<{
|
|
27
|
+
type: 'CVE' | 'GHSA';
|
|
28
|
+
value: string;
|
|
29
|
+
}>;
|
|
30
|
+
references: Array<{
|
|
31
|
+
url: string;
|
|
32
|
+
}>;
|
|
33
|
+
published_at: string;
|
|
34
|
+
updated_at: string;
|
|
35
|
+
vulnerabilities: Array<{
|
|
36
|
+
package: {
|
|
37
|
+
ecosystem: string;
|
|
38
|
+
name: string;
|
|
39
|
+
};
|
|
40
|
+
vulnerable_version_range: string;
|
|
41
|
+
first_patched_version: {
|
|
42
|
+
identifier: string;
|
|
43
|
+
} | null;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export interface GhsaQueryResult {
|
|
47
|
+
advisories: GhsaAdvisory[];
|
|
48
|
+
warnings: string[];
|
|
49
|
+
}
|
|
50
|
+
export interface GhsaQueryInput {
|
|
51
|
+
packages: Array<{
|
|
52
|
+
name: string;
|
|
53
|
+
ecosystem: 'npm' | 'pip' | 'go' | 'rust';
|
|
54
|
+
}>;
|
|
55
|
+
githubToken?: string;
|
|
56
|
+
}
|
|
57
|
+
export type FetchFn = (input: string, init?: {
|
|
58
|
+
headers?: Record<string, string>;
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
}) => Promise<{
|
|
61
|
+
ok: boolean;
|
|
62
|
+
status: number;
|
|
63
|
+
statusText?: string;
|
|
64
|
+
json: () => Promise<unknown>;
|
|
65
|
+
}>;
|
|
66
|
+
export interface GhsaClient {
|
|
67
|
+
queryGhsa(input: GhsaQueryInput): Promise<GhsaQueryResult>;
|
|
68
|
+
}
|
|
69
|
+
declare function mapAdvisory(raw: unknown): GhsaAdvisory | null;
|
|
70
|
+
declare function makeCacheKey(ecosystem: string, name: string): string;
|
|
71
|
+
export interface CreateGhsaClientOptions {
|
|
72
|
+
fetchFn?: FetchFn;
|
|
73
|
+
/** Override the cache TTL (ms). Mostly for tests. */
|
|
74
|
+
cacheTtlMs?: number;
|
|
75
|
+
/** Override the clock (ms). Mostly for tests. */
|
|
76
|
+
now?: () => number;
|
|
77
|
+
}
|
|
78
|
+
export declare function createGhsaClient(opts?: CreateGhsaClientOptions): GhsaClient;
|
|
79
|
+
export declare function queryGhsa(input: GhsaQueryInput): Promise<GhsaQueryResult>;
|
|
80
|
+
/** Test seam re-export for unit tests. */
|
|
81
|
+
export declare const _internal: {
|
|
82
|
+
mapAdvisory: typeof mapAdvisory;
|
|
83
|
+
makeCacheKey: typeof makeCacheKey;
|
|
84
|
+
CACHE_TTL_MS: number;
|
|
85
|
+
DEFAULT_TIMEOUT_MS: number;
|
|
86
|
+
};
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=ghsa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ghsa.d.ts","sourceRoot":"","sources":["../../src/feeds/ghsa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,UAAU,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,KAAK,CAAC;QACrB,OAAO,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QAC7C,wBAAwB,EAAE,MAAM,CAAC;QACjC,qBAAqB,EAAE;YAAE,UAAU,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;KACtD,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAOD,MAAM,MAAM,OAAO,GAAG,CACpB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAC9D,OAAO,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9B,CAAC,CAAC;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC5D;AAwCD,iBAAS,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAsEtD;AAWD,iBAAS,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D;AAuED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,uBAA4B,GAAG,UAAU,CAsC/E;AASD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAEzE;AAED,0CAA0C;AAC1C,eAAO,MAAM,SAAS;;;;;CAKrB,CAAC"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Security Advisory (GHSA) feed adapter.
|
|
3
|
+
*
|
|
4
|
+
* Cross-references installed dependencies against GitHub's security advisory
|
|
5
|
+
* database. Used alongside OSV by the deps scanner. The endpoint
|
|
6
|
+
* (`GET /advisories`) does not batch packages, so this module issues one
|
|
7
|
+
* request per `(ecosystem, name)` and merges/dedupes by `ghsa_id`.
|
|
8
|
+
*
|
|
9
|
+
* Resilience contract: this function never throws. Any fetch error or
|
|
10
|
+
* non-200 response is collected as a warning string; callers always receive
|
|
11
|
+
* a `GhsaQueryResult` with `advisories` and `warnings` arrays. An empty
|
|
12
|
+
* advisories list with a populated warnings list is the GHSA-down signal.
|
|
13
|
+
*
|
|
14
|
+
* @module feeds/ghsa
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
17
|
+
const CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
|
|
18
|
+
const PER_PAGE = 20;
|
|
19
|
+
const GHSA_BASE_URL = 'https://api.github.com/advisories';
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Type guards
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
function isRecord(value) {
|
|
24
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
25
|
+
}
|
|
26
|
+
function asString(value, fallback = '') {
|
|
27
|
+
return typeof value === 'string' ? value : fallback;
|
|
28
|
+
}
|
|
29
|
+
function asNullableString(value) {
|
|
30
|
+
return typeof value === 'string' ? value : null;
|
|
31
|
+
}
|
|
32
|
+
function asSeverity(value) {
|
|
33
|
+
if (value === 'low' || value === 'medium' || value === 'high' || value === 'critical') {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
// GHSA may emit 'moderate' historically; map to medium defensively.
|
|
37
|
+
if (value === 'moderate')
|
|
38
|
+
return 'medium';
|
|
39
|
+
return 'low';
|
|
40
|
+
}
|
|
41
|
+
function asIdentifierType(value) {
|
|
42
|
+
return value === 'CVE' || value === 'GHSA' ? value : null;
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Response mapping
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
function mapAdvisory(raw) {
|
|
48
|
+
if (!isRecord(raw))
|
|
49
|
+
return null;
|
|
50
|
+
const ghsaId = asString(raw.ghsa_id);
|
|
51
|
+
if (ghsaId.length === 0)
|
|
52
|
+
return null;
|
|
53
|
+
const identifiersRaw = Array.isArray(raw.identifiers) ? raw.identifiers : [];
|
|
54
|
+
const identifiers = [];
|
|
55
|
+
for (const item of identifiersRaw) {
|
|
56
|
+
if (!isRecord(item))
|
|
57
|
+
continue;
|
|
58
|
+
const type = asIdentifierType(item.type);
|
|
59
|
+
const value = asString(item.value);
|
|
60
|
+
if (type === null || value.length === 0)
|
|
61
|
+
continue;
|
|
62
|
+
identifiers.push({ type, value });
|
|
63
|
+
}
|
|
64
|
+
const referencesRaw = Array.isArray(raw.references) ? raw.references : [];
|
|
65
|
+
const references = [];
|
|
66
|
+
for (const item of referencesRaw) {
|
|
67
|
+
if (!isRecord(item))
|
|
68
|
+
continue;
|
|
69
|
+
const url = asString(item.url);
|
|
70
|
+
if (url.length === 0)
|
|
71
|
+
continue;
|
|
72
|
+
references.push({ url });
|
|
73
|
+
}
|
|
74
|
+
const vulnerabilitiesRaw = Array.isArray(raw.vulnerabilities) ? raw.vulnerabilities : [];
|
|
75
|
+
const vulnerabilities = [];
|
|
76
|
+
for (const item of vulnerabilitiesRaw) {
|
|
77
|
+
if (!isRecord(item))
|
|
78
|
+
continue;
|
|
79
|
+
const pkg = isRecord(item.package) ? item.package : null;
|
|
80
|
+
if (pkg === null)
|
|
81
|
+
continue;
|
|
82
|
+
const ecosystem = asString(pkg.ecosystem);
|
|
83
|
+
const name = asString(pkg.name);
|
|
84
|
+
if (ecosystem.length === 0 || name.length === 0)
|
|
85
|
+
continue;
|
|
86
|
+
const firstPatchedRaw = item.first_patched_version;
|
|
87
|
+
let firstPatched = null;
|
|
88
|
+
if (isRecord(firstPatchedRaw)) {
|
|
89
|
+
const identifier = asString(firstPatchedRaw.identifier);
|
|
90
|
+
if (identifier.length > 0)
|
|
91
|
+
firstPatched = { identifier };
|
|
92
|
+
}
|
|
93
|
+
vulnerabilities.push({
|
|
94
|
+
package: { ecosystem, name },
|
|
95
|
+
vulnerable_version_range: asString(item.vulnerable_version_range, '*'),
|
|
96
|
+
first_patched_version: firstPatched,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
let cvss;
|
|
100
|
+
if (isRecord(raw.cvss)) {
|
|
101
|
+
const score = typeof raw.cvss.score === 'number' ? raw.cvss.score : null;
|
|
102
|
+
const vectorString = asString(raw.cvss.vector_string);
|
|
103
|
+
if (score !== null && vectorString.length > 0) {
|
|
104
|
+
cvss = { score, vector_string: vectorString };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
ghsa_id: ghsaId,
|
|
109
|
+
cve_id: asNullableString(raw.cve_id),
|
|
110
|
+
summary: asString(raw.summary),
|
|
111
|
+
description: asString(raw.description),
|
|
112
|
+
severity: asSeverity(raw.severity),
|
|
113
|
+
cvss,
|
|
114
|
+
identifiers,
|
|
115
|
+
references,
|
|
116
|
+
published_at: asString(raw.published_at),
|
|
117
|
+
updated_at: asString(raw.updated_at),
|
|
118
|
+
vulnerabilities,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function makeCacheKey(ecosystem, name) {
|
|
122
|
+
return `${ecosystem}:${name}`;
|
|
123
|
+
}
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// HTTP -- one request per package
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
async function fetchAdvisoriesForPackage(fetchFn, ecosystem, name, githubToken, warnings) {
|
|
128
|
+
const url = `${GHSA_BASE_URL}?ecosystem=${encodeURIComponent(ecosystem)}` +
|
|
129
|
+
`&affects=${encodeURIComponent(name)}` +
|
|
130
|
+
`&per_page=${PER_PAGE}`;
|
|
131
|
+
const headers = {
|
|
132
|
+
Accept: 'application/vnd.github+json',
|
|
133
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
134
|
+
};
|
|
135
|
+
if (githubToken && githubToken.length > 0) {
|
|
136
|
+
headers.Authorization = `Bearer ${githubToken}`;
|
|
137
|
+
}
|
|
138
|
+
const controller = new AbortController();
|
|
139
|
+
const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
140
|
+
try {
|
|
141
|
+
const response = await fetchFn(url, { headers, signal: controller.signal });
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
if (response.status === 429) {
|
|
144
|
+
warnings.push(`ghsa: rate limited (HTTP 429) for ${ecosystem}:${name}; consider passing githubToken`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const statusText = response.statusText ?? '';
|
|
148
|
+
warnings.push(`ghsa: HTTP ${response.status}${statusText ? ' ' + statusText : ''} for ${ecosystem}:${name}`);
|
|
149
|
+
}
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
const body = await response.json();
|
|
153
|
+
if (!Array.isArray(body)) {
|
|
154
|
+
warnings.push(`ghsa: unexpected response shape for ${ecosystem}:${name}`);
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
const advisories = [];
|
|
158
|
+
for (const item of body) {
|
|
159
|
+
const mapped = mapAdvisory(item);
|
|
160
|
+
if (mapped !== null)
|
|
161
|
+
advisories.push(mapped);
|
|
162
|
+
}
|
|
163
|
+
return advisories;
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
167
|
+
warnings.push(`ghsa: fetch failed for ${ecosystem}:${name}: ${message}`);
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export function createGhsaClient(opts = {}) {
|
|
175
|
+
const fetchFn = opts.fetchFn ?? ((input, init) => fetch(input, init));
|
|
176
|
+
const ttl = opts.cacheTtlMs ?? CACHE_TTL_MS;
|
|
177
|
+
const now = opts.now ?? (() => Date.now());
|
|
178
|
+
const cache = new Map();
|
|
179
|
+
return {
|
|
180
|
+
async queryGhsa(input) {
|
|
181
|
+
const warnings = [];
|
|
182
|
+
const byId = new Map();
|
|
183
|
+
const t = now();
|
|
184
|
+
for (const pkg of input.packages) {
|
|
185
|
+
const key = makeCacheKey(pkg.ecosystem, pkg.name);
|
|
186
|
+
const cached = cache.get(key);
|
|
187
|
+
let advisories;
|
|
188
|
+
if (cached && cached.expiresAt > t) {
|
|
189
|
+
advisories = cached.advisories;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
advisories = await fetchAdvisoriesForPackage(fetchFn, pkg.ecosystem, pkg.name, input.githubToken, warnings);
|
|
193
|
+
cache.set(key, { expiresAt: now() + ttl, advisories });
|
|
194
|
+
}
|
|
195
|
+
for (const adv of advisories) {
|
|
196
|
+
if (!byId.has(adv.ghsa_id))
|
|
197
|
+
byId.set(adv.ghsa_id, adv);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { advisories: Array.from(byId.values()), warnings };
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Module-level default client -- preserves the simple `queryGhsa(input)` API
|
|
206
|
+
// while real callers go through the spawn-backed default fetch.
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
const defaultClient = createGhsaClient();
|
|
209
|
+
export function queryGhsa(input) {
|
|
210
|
+
return defaultClient.queryGhsa(input);
|
|
211
|
+
}
|
|
212
|
+
/** Test seam re-export for unit tests. */
|
|
213
|
+
export const _internal = {
|
|
214
|
+
mapAdvisory,
|
|
215
|
+
makeCacheKey,
|
|
216
|
+
CACHE_TTL_MS,
|
|
217
|
+
DEFAULT_TIMEOUT_MS,
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=ghsa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ghsa.js","sourceRoot":"","sources":["../../src/feeds/ghsa.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local IOC feed adapter.
|
|
3
|
+
*
|
|
4
|
+
* Loads org-local custom IOC (indicator of compromise) entries from the
|
|
5
|
+
* `security_ioc_feed_entries` table via the daemon edge function. Local feed
|
|
6
|
+
* entries let operators inject detection signal ahead of central OSV/GHSA
|
|
7
|
+
* indexing -- the security scanners (deps, workflow) cross-reference findings
|
|
8
|
+
* against the patterns this module returns.
|
|
9
|
+
*
|
|
10
|
+
* Pattern semantics:
|
|
11
|
+
* - 'string' -- exact equality match.
|
|
12
|
+
* - 'regex' -- JS RegExp test; invalid patterns are non-matches (not errors).
|
|
13
|
+
* - 'sha256' -- case-insensitive hex equality (caller hashes the value).
|
|
14
|
+
* - 'cidr' -- IPv4 CIDR membership; IPv6 / malformed inputs are non-matches.
|
|
15
|
+
*
|
|
16
|
+
* @module feeds/local
|
|
17
|
+
*/
|
|
18
|
+
export type IocPatternType = 'string' | 'regex' | 'sha256' | 'cidr';
|
|
19
|
+
export type IocSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
20
|
+
export interface IocFeedEntry {
|
|
21
|
+
id: string;
|
|
22
|
+
organizationId: string;
|
|
23
|
+
iocClass: string;
|
|
24
|
+
patternType: IocPatternType;
|
|
25
|
+
pattern: string;
|
|
26
|
+
severity: IocSeverity;
|
|
27
|
+
advisoryText: string | null;
|
|
28
|
+
addedAt: string;
|
|
29
|
+
expiresAt: string | null;
|
|
30
|
+
}
|
|
31
|
+
type CallApi = <T>(action: string, params?: Record<string, unknown>) => Promise<T>;
|
|
32
|
+
export interface LocalFeedClient {
|
|
33
|
+
loadLocalIocFeed(organizationId: string, iocClass?: string): Promise<IocFeedEntry[]>;
|
|
34
|
+
}
|
|
35
|
+
interface LocalFeedClientDeps {
|
|
36
|
+
callApi: CallApi;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build a local feed client bound to a given `callApi` implementation.
|
|
40
|
+
* Tests use this to inject a stub; production code uses the default export.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createLocalFeedClient(deps: LocalFeedClientDeps): LocalFeedClient;
|
|
43
|
+
/**
|
|
44
|
+
* Load non-expired local IOC feed entries for an organization.
|
|
45
|
+
* The edge function filters out expired rows; this adapter just maps.
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadLocalIocFeed(organizationId: string, iocClass?: string): Promise<IocFeedEntry[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Check whether `value` matches the IOC `entry`'s pattern. Never throws --
|
|
50
|
+
* malformed patterns or inputs return false so a single bad feed entry can't
|
|
51
|
+
* abort an entire scan.
|
|
52
|
+
*/
|
|
53
|
+
export declare function matchesPattern(value: string, entry: IocFeedEntry): boolean;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/feeds/local.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,cAAc,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAMD,KAAK,OAAO,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEnF,MAAM,WAAW,eAAe;IAC9B,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CACtF;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,OAAO,CAAC;CAClB;AA4ED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,GAAG,eAAe,CAehF;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC,CAKzB;AAMD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAa1E"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local IOC feed adapter.
|
|
3
|
+
*
|
|
4
|
+
* Loads org-local custom IOC (indicator of compromise) entries from the
|
|
5
|
+
* `security_ioc_feed_entries` table via the daemon edge function. Local feed
|
|
6
|
+
* entries let operators inject detection signal ahead of central OSV/GHSA
|
|
7
|
+
* indexing -- the security scanners (deps, workflow) cross-reference findings
|
|
8
|
+
* against the patterns this module returns.
|
|
9
|
+
*
|
|
10
|
+
* Pattern semantics:
|
|
11
|
+
* - 'string' -- exact equality match.
|
|
12
|
+
* - 'regex' -- JS RegExp test; invalid patterns are non-matches (not errors).
|
|
13
|
+
* - 'sha256' -- case-insensitive hex equality (caller hashes the value).
|
|
14
|
+
* - 'cidr' -- IPv4 CIDR membership; IPv6 / malformed inputs are non-matches.
|
|
15
|
+
*
|
|
16
|
+
* @module feeds/local
|
|
17
|
+
*/
|
|
18
|
+
import { callApi as defaultCallApi } from '../queries/shared.js';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Row narrowing
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const VALID_PATTERN_TYPES = new Set([
|
|
23
|
+
'string',
|
|
24
|
+
'regex',
|
|
25
|
+
'sha256',
|
|
26
|
+
'cidr',
|
|
27
|
+
]);
|
|
28
|
+
const VALID_SEVERITIES = new Set([
|
|
29
|
+
'low',
|
|
30
|
+
'medium',
|
|
31
|
+
'high',
|
|
32
|
+
'critical',
|
|
33
|
+
]);
|
|
34
|
+
function isRecord(value) {
|
|
35
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
function asString(value) {
|
|
38
|
+
return typeof value === 'string' ? value : null;
|
|
39
|
+
}
|
|
40
|
+
function asNullableString(value) {
|
|
41
|
+
if (value === null || value === undefined)
|
|
42
|
+
return null;
|
|
43
|
+
return typeof value === 'string' ? value : null;
|
|
44
|
+
}
|
|
45
|
+
function toFeedEntry(row) {
|
|
46
|
+
if (!isRecord(row))
|
|
47
|
+
return null;
|
|
48
|
+
const id = asString(row.id);
|
|
49
|
+
const organizationId = asString(row.organization_id);
|
|
50
|
+
const iocClass = asString(row.ioc_class);
|
|
51
|
+
const patternTypeRaw = asString(row.pattern_type);
|
|
52
|
+
const pattern = asString(row.pattern);
|
|
53
|
+
const severityRaw = asString(row.severity);
|
|
54
|
+
if (!id || !organizationId || !iocClass || !patternTypeRaw || !pattern || !severityRaw) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (!VALID_PATTERN_TYPES.has(patternTypeRaw))
|
|
58
|
+
return null;
|
|
59
|
+
if (!VALID_SEVERITIES.has(severityRaw))
|
|
60
|
+
return null;
|
|
61
|
+
const addedAt = asString(row.added_at);
|
|
62
|
+
if (!addedAt)
|
|
63
|
+
return null;
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
organizationId,
|
|
67
|
+
iocClass,
|
|
68
|
+
patternType: patternTypeRaw,
|
|
69
|
+
pattern,
|
|
70
|
+
severity: severityRaw,
|
|
71
|
+
advisoryText: asNullableString(row.advisory_text),
|
|
72
|
+
addedAt,
|
|
73
|
+
expiresAt: asNullableString(row.expires_at),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function extractRows(response) {
|
|
77
|
+
if (Array.isArray(response))
|
|
78
|
+
return response;
|
|
79
|
+
if (isRecord(response) && Array.isArray(response.items))
|
|
80
|
+
return response.items;
|
|
81
|
+
if (isRecord(response) && Array.isArray(response.entries))
|
|
82
|
+
return response.entries;
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// loadLocalIocFeed (production wrapper + DI factory)
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Build a local feed client bound to a given `callApi` implementation.
|
|
90
|
+
* Tests use this to inject a stub; production code uses the default export.
|
|
91
|
+
*/
|
|
92
|
+
export function createLocalFeedClient(deps) {
|
|
93
|
+
return {
|
|
94
|
+
async loadLocalIocFeed(organizationId, iocClass) {
|
|
95
|
+
const params = { organizationId };
|
|
96
|
+
if (iocClass !== undefined)
|
|
97
|
+
params.iocClass = iocClass;
|
|
98
|
+
const response = await deps.callApi('daemon_load_local_ioc_feed', params);
|
|
99
|
+
const rows = extractRows(response);
|
|
100
|
+
const entries = [];
|
|
101
|
+
for (const row of rows) {
|
|
102
|
+
const entry = toFeedEntry(row);
|
|
103
|
+
if (entry)
|
|
104
|
+
entries.push(entry);
|
|
105
|
+
}
|
|
106
|
+
return entries;
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Load non-expired local IOC feed entries for an organization.
|
|
112
|
+
* The edge function filters out expired rows; this adapter just maps.
|
|
113
|
+
*/
|
|
114
|
+
export async function loadLocalIocFeed(organizationId, iocClass) {
|
|
115
|
+
return createLocalFeedClient({ callApi: defaultCallApi }).loadLocalIocFeed(organizationId, iocClass);
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// matchesPattern
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
/**
|
|
121
|
+
* Check whether `value` matches the IOC `entry`'s pattern. Never throws --
|
|
122
|
+
* malformed patterns or inputs return false so a single bad feed entry can't
|
|
123
|
+
* abort an entire scan.
|
|
124
|
+
*/
|
|
125
|
+
export function matchesPattern(value, entry) {
|
|
126
|
+
switch (entry.patternType) {
|
|
127
|
+
case 'string':
|
|
128
|
+
return value === entry.pattern;
|
|
129
|
+
case 'regex':
|
|
130
|
+
return regexMatches(value, entry.pattern);
|
|
131
|
+
case 'sha256':
|
|
132
|
+
return value.toLowerCase() === entry.pattern.toLowerCase();
|
|
133
|
+
case 'cidr':
|
|
134
|
+
return ipv4InCidr(value, entry.pattern);
|
|
135
|
+
default:
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function regexMatches(value, pattern) {
|
|
140
|
+
try {
|
|
141
|
+
return new RegExp(pattern).test(value);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// IPv4 CIDR membership (inline; no deps)
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
function parseIpv4(ip) {
|
|
151
|
+
// Reject anything containing a colon (IPv6) up front.
|
|
152
|
+
if (ip.includes(':'))
|
|
153
|
+
return null;
|
|
154
|
+
const parts = ip.split('.');
|
|
155
|
+
if (parts.length !== 4)
|
|
156
|
+
return null;
|
|
157
|
+
let result = 0;
|
|
158
|
+
for (const part of parts) {
|
|
159
|
+
// Require pure decimal octets with no leading '+', signs, or whitespace.
|
|
160
|
+
if (!/^\d+$/.test(part))
|
|
161
|
+
return null;
|
|
162
|
+
const n = Number(part);
|
|
163
|
+
if (!Number.isInteger(n) || n < 0 || n > 255)
|
|
164
|
+
return null;
|
|
165
|
+
result = (result * 256) + n;
|
|
166
|
+
}
|
|
167
|
+
// Force unsigned 32-bit
|
|
168
|
+
return result >>> 0;
|
|
169
|
+
}
|
|
170
|
+
function ipv4InCidr(value, cidr) {
|
|
171
|
+
// Reject IPv6 CIDR explicitly.
|
|
172
|
+
if (cidr.includes(':'))
|
|
173
|
+
return false;
|
|
174
|
+
const slash = cidr.indexOf('/');
|
|
175
|
+
if (slash < 0)
|
|
176
|
+
return false;
|
|
177
|
+
const networkStr = cidr.slice(0, slash);
|
|
178
|
+
const prefixStr = cidr.slice(slash + 1);
|
|
179
|
+
if (!/^\d+$/.test(prefixStr))
|
|
180
|
+
return false;
|
|
181
|
+
const prefix = Number(prefixStr);
|
|
182
|
+
if (!Number.isInteger(prefix) || prefix < 0 || prefix > 32)
|
|
183
|
+
return false;
|
|
184
|
+
const network = parseIpv4(networkStr);
|
|
185
|
+
if (network === null)
|
|
186
|
+
return false;
|
|
187
|
+
const ip = parseIpv4(value);
|
|
188
|
+
if (ip === null)
|
|
189
|
+
return false;
|
|
190
|
+
if (prefix === 0)
|
|
191
|
+
return true; // 0.0.0.0/0 matches anything IPv4.
|
|
192
|
+
// Build mask: top `prefix` bits set.
|
|
193
|
+
const mask = (0xffffffff << (32 - prefix)) >>> 0;
|
|
194
|
+
return (network & mask) === (ip & mask);
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/feeds/local.ts"],"names":[],"mappings":"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"}
|