@weave_protocol/mund 0.2.3 → 0.3.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.
- package/README.md +94 -0
- package/dist/analyzers/code-analyzer.d.ts.map +1 -1
- package/dist/analyzers/code-analyzer.js +2 -1
- package/dist/analyzers/code-analyzer.js.map +1 -1
- package/dist/analyzers/exfiltration-detector.d.ts.map +1 -1
- package/dist/analyzers/exfiltration-detector.js +2 -1
- package/dist/analyzers/exfiltration-detector.js.map +1 -1
- package/dist/analyzers/index.js +1 -1
- package/dist/analyzers/injection-detector.d.ts.map +1 -1
- package/dist/analyzers/injection-detector.js +2 -1
- package/dist/analyzers/injection-detector.js.map +1 -1
- package/dist/analyzers/mcp-server-analyzer.d.ts +2 -0
- package/dist/analyzers/mcp-server-analyzer.d.ts.map +1 -1
- package/dist/analyzers/mcp-server-analyzer.js.map +1 -1
- package/dist/analyzers/pii-detector.d.ts.map +1 -1
- package/dist/analyzers/pii-detector.js +2 -1
- package/dist/analyzers/pii-detector.js.map +1 -1
- package/dist/analyzers/regex.d.ts +10 -0
- package/dist/analyzers/regex.d.ts.map +1 -0
- package/dist/analyzers/regex.js +17 -0
- package/dist/analyzers/regex.js.map +1 -0
- package/dist/analyzers/secret-scanner.d.ts.map +1 -1
- package/dist/analyzers/secret-scanner.js +2 -1
- package/dist/analyzers/secret-scanner.js.map +1 -1
- package/dist/cli/scan.d.ts +49 -0
- package/dist/cli/scan.d.ts.map +1 -0
- package/dist/cli/scan.js +226 -0
- package/dist/cli/scan.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/notifications/email.d.ts +21 -8
- package/dist/notifications/email.d.ts.map +1 -1
- package/dist/notifications/email.js +32 -178
- package/dist/notifications/email.js.map +1 -1
- package/dist/osv.d.ts +94 -0
- package/dist/osv.d.ts.map +1 -0
- package/dist/osv.js +239 -0
- package/dist/osv.js.map +1 -0
- package/dist/sarif.d.ts +20 -0
- package/dist/sarif.d.ts.map +1 -0
- package/dist/sarif.js +122 -0
- package/dist/sarif.js.map +1 -0
- package/dist/scoring.d.ts +26 -0
- package/dist/scoring.d.ts.map +1 -0
- package/dist/scoring.js +49 -0
- package/dist/scoring.js.map +1 -0
- package/dist/threat-intel-manager.d.ts.map +1 -1
- package/dist/threat-intel-manager.js +2 -1
- package/dist/threat-intel-manager.js.map +1 -1
- package/dist/tools/configuration.d.ts.map +1 -1
- package/dist/tools/configuration.js +2 -1
- package/dist/tools/configuration.js.map +1 -1
- package/dist/tools/mcp-scanning.d.ts.map +1 -1
- package/dist/tools/mcp-scanning.js +4 -0
- package/dist/tools/mcp-scanning.js.map +1 -1
- package/dist/tools/monitoring.d.ts.map +1 -1
- package/dist/tools/monitoring.js +22 -1
- package/dist/tools/monitoring.js.map +1 -1
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +19 -0
- package/dist/version.js.map +1 -0
- package/package.json +2 -4
- package/src/analyzers/code-analyzer.ts +2 -1
- package/src/analyzers/exfiltration-detector.ts +2 -1
- package/src/analyzers/index.ts +1 -1
- package/src/analyzers/injection-detector.ts +2 -1
- package/src/analyzers/mcp-server-analyzer.ts +2 -0
- package/src/analyzers/pii-detector.ts +2 -1
- package/src/analyzers/regex.ts +17 -0
- package/src/analyzers/secret-scanner.ts +2 -1
- package/src/cli/scan.ts +251 -0
- package/src/index.ts +11 -4
- package/src/notifications/email.ts +38 -194
- package/src/osv.ts +310 -0
- package/src/sarif.ts +146 -0
- package/src/scoring.ts +64 -0
- package/src/threat-intel-manager.ts +2 -1
- package/src/tools/configuration.ts +2 -1
- package/src/tools/mcp-scanning.ts +5 -0
- package/src/tools/monitoring.ts +23 -1
- package/src/version.ts +20 -0
- package/tests/analyzers/engine.test.ts +60 -0
- package/tests/analyzers/injection-and-code.test.ts +80 -0
- package/tests/analyzers/mcp-server-analyzer.test.ts +82 -0
- package/tests/analyzers/regex.test.ts +35 -0
- package/tests/analyzers/rules.test.ts +54 -0
- package/tests/analyzers/secret-scanner.test.ts +80 -0
- package/tests/cli/scan.test.ts +92 -0
- package/tests/helpers.ts +51 -0
- package/tests/notifications/email.test.ts +68 -0
- package/tests/notifications/hub.test.ts +160 -0
- package/tests/osv.test.ts +117 -0
- package/tests/sarif.test.ts +59 -0
- package/tests/scoring.test.ts +49 -0
- package/tests/tools/monitoring.test.ts +95 -0
- package/vitest.config.ts +8 -0
package/dist/osv.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mund - The Guardian Protocol
|
|
3
|
+
* OSV.dev dependency vulnerability lookups with an offline fallback
|
|
4
|
+
*
|
|
5
|
+
* Live path: POST /v1/querybatch for every name@version in a lockfile,
|
|
6
|
+
* then GET /v1/vulns/{id} once per unique advisory for details.
|
|
7
|
+
*
|
|
8
|
+
* Offline path: every successful live lookup is written to an on-disk
|
|
9
|
+
* cache. When the network is unavailable (or MUND_OFFLINE=1 / --offline),
|
|
10
|
+
* results come from that cache and are reported with source "cache", so
|
|
11
|
+
* callers can tell a clean result from an unchecked one. Packages that
|
|
12
|
+
* were never looked up are reported as unchecked, never as clean.
|
|
13
|
+
*/
|
|
14
|
+
import type { SecurityIssue, Severity } from './types.js';
|
|
15
|
+
export interface LockedPackage {
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
/** Lockfile key, e.g. "node_modules/qs", used to locate the finding. */
|
|
19
|
+
path?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface OsvAdvisory {
|
|
22
|
+
id: string;
|
|
23
|
+
summary: string;
|
|
24
|
+
severity: Severity;
|
|
25
|
+
aliases: string[];
|
|
26
|
+
fixed?: string;
|
|
27
|
+
url: string;
|
|
28
|
+
}
|
|
29
|
+
export type OsvSource = 'live' | 'cache' | 'unavailable';
|
|
30
|
+
export interface OsvLookupResult {
|
|
31
|
+
source: OsvSource;
|
|
32
|
+
/** Keyed by "name@version". */
|
|
33
|
+
advisories: Map<string, OsvAdvisory[]>;
|
|
34
|
+
/** name@version keys with no live or cached answer. */
|
|
35
|
+
unchecked: string[];
|
|
36
|
+
/** Why the live lookup did not run or failed, if it didn't. */
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface OsvClientOptions {
|
|
40
|
+
offline?: boolean;
|
|
41
|
+
cachePath?: string;
|
|
42
|
+
timeoutMs?: number;
|
|
43
|
+
fetch?: typeof fetch;
|
|
44
|
+
}
|
|
45
|
+
interface OsvVuln {
|
|
46
|
+
id: string;
|
|
47
|
+
summary?: string;
|
|
48
|
+
details?: string;
|
|
49
|
+
aliases?: string[];
|
|
50
|
+
severity?: Array<{
|
|
51
|
+
type: string;
|
|
52
|
+
score: string;
|
|
53
|
+
}>;
|
|
54
|
+
database_specific?: {
|
|
55
|
+
severity?: string;
|
|
56
|
+
};
|
|
57
|
+
affected?: Array<{
|
|
58
|
+
package?: {
|
|
59
|
+
name: string;
|
|
60
|
+
ecosystem: string;
|
|
61
|
+
};
|
|
62
|
+
ranges?: Array<{
|
|
63
|
+
events: Array<{
|
|
64
|
+
introduced?: string;
|
|
65
|
+
fixed?: string;
|
|
66
|
+
}>;
|
|
67
|
+
}>;
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
export declare function defaultCachePath(): string;
|
|
71
|
+
/** Parse an npm package-lock.json (lockfileVersion 1, 2 or 3). */
|
|
72
|
+
export declare function parseNpmLockfile(content: string): LockedPackage[];
|
|
73
|
+
export declare class OsvClient {
|
|
74
|
+
private readonly offline;
|
|
75
|
+
private readonly cachePath;
|
|
76
|
+
private readonly timeoutMs;
|
|
77
|
+
private readonly fetchImpl;
|
|
78
|
+
constructor(options?: OsvClientOptions);
|
|
79
|
+
lookup(packages: LockedPackage[], ecosystem?: string): Promise<OsvLookupResult>;
|
|
80
|
+
private lookupLive;
|
|
81
|
+
private lookupCached;
|
|
82
|
+
private request;
|
|
83
|
+
private readCache;
|
|
84
|
+
private writeCache;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* OSV `severity[].score` is usually a CVSS *vector* string, not a number,
|
|
88
|
+
* so prefer the database's own label (GHSA sets it for npm advisories).
|
|
89
|
+
*/
|
|
90
|
+
export declare function parseSeverity(vuln: Pick<OsvVuln, 'severity' | 'database_specific'>): Severity;
|
|
91
|
+
/** Turn lookup results into SecurityIssues located in the lockfile. */
|
|
92
|
+
export declare function advisoriesToIssues(packages: LockedPackage[], result: OsvLookupResult, lockfileContent: string): SecurityIssue[];
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=osv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osv.d.ts","sourceRoot":"","sources":["../src/osv.ts"],"names":[],"mappings":"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"}
|
package/dist/osv.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mund - The Guardian Protocol
|
|
3
|
+
* OSV.dev dependency vulnerability lookups with an offline fallback
|
|
4
|
+
*
|
|
5
|
+
* Live path: POST /v1/querybatch for every name@version in a lockfile,
|
|
6
|
+
* then GET /v1/vulns/{id} once per unique advisory for details.
|
|
7
|
+
*
|
|
8
|
+
* Offline path: every successful live lookup is written to an on-disk
|
|
9
|
+
* cache. When the network is unavailable (or MUND_OFFLINE=1 / --offline),
|
|
10
|
+
* results come from that cache and are reported with source "cache", so
|
|
11
|
+
* callers can tell a clean result from an unchecked one. Packages that
|
|
12
|
+
* were never looked up are reported as unchecked, never as clean.
|
|
13
|
+
*/
|
|
14
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
15
|
+
import { homedir } from 'os';
|
|
16
|
+
import { dirname, join } from 'path';
|
|
17
|
+
const OSV_API = 'https://api.osv.dev/v1';
|
|
18
|
+
const BATCH_SIZE = 1000;
|
|
19
|
+
const DETAIL_CONCURRENCY = 8;
|
|
20
|
+
export function defaultCachePath() {
|
|
21
|
+
if (process.env.MUND_OSV_CACHE)
|
|
22
|
+
return process.env.MUND_OSV_CACHE;
|
|
23
|
+
const base = process.env.XDG_CACHE_HOME || join(homedir(), '.cache');
|
|
24
|
+
return join(base, 'mund', 'osv-cache.json');
|
|
25
|
+
}
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Lockfile parsing
|
|
28
|
+
// ============================================================================
|
|
29
|
+
/** Parse an npm package-lock.json (lockfileVersion 1, 2 or 3). */
|
|
30
|
+
export function parseNpmLockfile(content) {
|
|
31
|
+
const lock = JSON.parse(content);
|
|
32
|
+
const seen = new Map();
|
|
33
|
+
const add = (pkg) => {
|
|
34
|
+
const key = `${pkg.name}@${pkg.version}`;
|
|
35
|
+
if (!seen.has(key))
|
|
36
|
+
seen.set(key, pkg);
|
|
37
|
+
};
|
|
38
|
+
if (lock.packages) {
|
|
39
|
+
for (const [path, entry] of Object.entries(lock.packages)) {
|
|
40
|
+
if (!path || entry.link || !entry.version)
|
|
41
|
+
continue;
|
|
42
|
+
const idx = path.lastIndexOf('node_modules/');
|
|
43
|
+
if (idx === -1)
|
|
44
|
+
continue; // workspace package, not a dependency
|
|
45
|
+
add({ name: entry.name ?? path.slice(idx + 'node_modules/'.length), version: entry.version, path });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (lock.dependencies) {
|
|
49
|
+
const walk = (deps) => {
|
|
50
|
+
for (const [name, dep] of Object.entries(deps)) {
|
|
51
|
+
if (dep.version && !dep.version.startsWith('file:'))
|
|
52
|
+
add({ name, version: dep.version });
|
|
53
|
+
if (dep.dependencies)
|
|
54
|
+
walk(dep.dependencies);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
walk(lock.dependencies);
|
|
58
|
+
}
|
|
59
|
+
return [...seen.values()];
|
|
60
|
+
}
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Client
|
|
63
|
+
// ============================================================================
|
|
64
|
+
export class OsvClient {
|
|
65
|
+
offline;
|
|
66
|
+
cachePath;
|
|
67
|
+
timeoutMs;
|
|
68
|
+
fetchImpl;
|
|
69
|
+
constructor(options = {}) {
|
|
70
|
+
this.offline = options.offline ?? process.env.MUND_OFFLINE === '1';
|
|
71
|
+
this.cachePath = options.cachePath ?? defaultCachePath();
|
|
72
|
+
this.timeoutMs = options.timeoutMs ?? 15000;
|
|
73
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
74
|
+
}
|
|
75
|
+
async lookup(packages, ecosystem = 'npm') {
|
|
76
|
+
const keys = packages.map(p => `${p.name}@${p.version}`);
|
|
77
|
+
if (packages.length === 0)
|
|
78
|
+
return { source: 'live', advisories: new Map(), unchecked: [] };
|
|
79
|
+
if (!this.offline) {
|
|
80
|
+
try {
|
|
81
|
+
const advisories = await this.lookupLive(packages, ecosystem);
|
|
82
|
+
await this.writeCache(advisories);
|
|
83
|
+
return { source: 'live', advisories, unchecked: [] };
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return this.lookupCached(keys, `OSV.dev unreachable: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return this.lookupCached(keys, 'offline mode');
|
|
90
|
+
}
|
|
91
|
+
async lookupLive(packages, ecosystem) {
|
|
92
|
+
const idsByKey = new Map();
|
|
93
|
+
for (let i = 0; i < packages.length; i += BATCH_SIZE) {
|
|
94
|
+
const chunk = packages.slice(i, i + BATCH_SIZE);
|
|
95
|
+
const body = { queries: chunk.map(p => ({ package: { name: p.name, ecosystem }, version: p.version })) };
|
|
96
|
+
const data = await this.request(`${OSV_API}/querybatch`, { method: 'POST', body: JSON.stringify(body) });
|
|
97
|
+
chunk.forEach((p, j) => {
|
|
98
|
+
idsByKey.set(`${p.name}@${p.version}`, (data.results[j]?.vulns ?? []).map(v => v.id));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const uniqueIds = [...new Set([...idsByKey.values()].flat())];
|
|
102
|
+
const details = new Map();
|
|
103
|
+
for (let i = 0; i < uniqueIds.length; i += DETAIL_CONCURRENCY) {
|
|
104
|
+
const batch = uniqueIds.slice(i, i + DETAIL_CONCURRENCY);
|
|
105
|
+
const fetched = await Promise.all(batch.map(id => this.request(`${OSV_API}/vulns/${encodeURIComponent(id)}`)));
|
|
106
|
+
fetched.forEach(v => details.set(v.id, v));
|
|
107
|
+
}
|
|
108
|
+
const advisories = new Map();
|
|
109
|
+
for (const [key, ids] of idsByKey) {
|
|
110
|
+
const name = key.slice(0, key.lastIndexOf('@'));
|
|
111
|
+
advisories.set(key, ids.map(id => toAdvisory(details.get(id) ?? { id }, name)));
|
|
112
|
+
}
|
|
113
|
+
return advisories;
|
|
114
|
+
}
|
|
115
|
+
async lookupCached(keys, reason) {
|
|
116
|
+
const cache = await this.readCache();
|
|
117
|
+
const advisories = new Map();
|
|
118
|
+
const unchecked = [];
|
|
119
|
+
for (const key of keys) {
|
|
120
|
+
const hit = cache?.packages[key];
|
|
121
|
+
if (hit)
|
|
122
|
+
advisories.set(key, hit.advisories);
|
|
123
|
+
else
|
|
124
|
+
unchecked.push(key);
|
|
125
|
+
}
|
|
126
|
+
const source = advisories.size > 0 ? 'cache' : 'unavailable';
|
|
127
|
+
return { source, advisories, unchecked, error: reason };
|
|
128
|
+
}
|
|
129
|
+
async request(url, init = {}) {
|
|
130
|
+
const res = await this.fetchImpl(url, {
|
|
131
|
+
...init,
|
|
132
|
+
headers: { 'Content-Type': 'application/json' },
|
|
133
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
134
|
+
});
|
|
135
|
+
if (!res.ok)
|
|
136
|
+
throw new Error(`${res.status} ${res.statusText} from ${url}`);
|
|
137
|
+
return (await res.json());
|
|
138
|
+
}
|
|
139
|
+
async readCache() {
|
|
140
|
+
try {
|
|
141
|
+
const cache = JSON.parse(await readFile(this.cachePath, 'utf8'));
|
|
142
|
+
return cache.version === 1 && cache.packages ? cache : null;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async writeCache(advisories) {
|
|
149
|
+
const cache = (await this.readCache()) ?? { version: 1, packages: {} };
|
|
150
|
+
const checkedAt = new Date().toISOString();
|
|
151
|
+
for (const [key, list] of advisories)
|
|
152
|
+
cache.packages[key] = { checkedAt, advisories: list };
|
|
153
|
+
try {
|
|
154
|
+
await mkdir(dirname(this.cachePath), { recursive: true });
|
|
155
|
+
await writeFile(this.cachePath, JSON.stringify(cache));
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// A read-only cache dir shouldn't fail the scan; we just lose the fallback.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function toAdvisory(vuln, packageName) {
|
|
163
|
+
let fixed;
|
|
164
|
+
for (const affected of vuln.affected ?? []) {
|
|
165
|
+
if (affected.package && affected.package.name !== packageName)
|
|
166
|
+
continue;
|
|
167
|
+
for (const range of affected.ranges ?? []) {
|
|
168
|
+
for (const event of range.events)
|
|
169
|
+
if (event.fixed)
|
|
170
|
+
fixed = event.fixed;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
id: vuln.id,
|
|
175
|
+
summary: vuln.summary || vuln.details?.split('\n')[0]?.slice(0, 200) || 'No summary available',
|
|
176
|
+
severity: parseSeverity(vuln),
|
|
177
|
+
aliases: vuln.aliases ?? [],
|
|
178
|
+
fixed,
|
|
179
|
+
url: `https://osv.dev/vulnerability/${encodeURIComponent(vuln.id)}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* OSV `severity[].score` is usually a CVSS *vector* string, not a number,
|
|
184
|
+
* so prefer the database's own label (GHSA sets it for npm advisories).
|
|
185
|
+
*/
|
|
186
|
+
export function parseSeverity(vuln) {
|
|
187
|
+
const label = vuln.database_specific?.severity?.toLowerCase();
|
|
188
|
+
if (label === 'critical')
|
|
189
|
+
return 'critical';
|
|
190
|
+
if (label === 'high')
|
|
191
|
+
return 'high';
|
|
192
|
+
if (label === 'moderate' || label === 'medium')
|
|
193
|
+
return 'medium';
|
|
194
|
+
if (label === 'low')
|
|
195
|
+
return 'low';
|
|
196
|
+
for (const s of vuln.severity ?? []) {
|
|
197
|
+
const score = Number(s.score);
|
|
198
|
+
if (!Number.isNaN(score)) {
|
|
199
|
+
if (score >= 9)
|
|
200
|
+
return 'critical';
|
|
201
|
+
if (score >= 7)
|
|
202
|
+
return 'high';
|
|
203
|
+
if (score >= 4)
|
|
204
|
+
return 'medium';
|
|
205
|
+
if (score > 0)
|
|
206
|
+
return 'low';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return 'medium';
|
|
210
|
+
}
|
|
211
|
+
// ============================================================================
|
|
212
|
+
// Findings
|
|
213
|
+
// ============================================================================
|
|
214
|
+
/** Turn lookup results into SecurityIssues located in the lockfile. */
|
|
215
|
+
export function advisoriesToIssues(packages, result, lockfileContent) {
|
|
216
|
+
const issues = [];
|
|
217
|
+
for (const pkg of packages) {
|
|
218
|
+
const key = `${pkg.name}@${pkg.version}`;
|
|
219
|
+
for (const adv of result.advisories.get(key) ?? []) {
|
|
220
|
+
const needle = pkg.path ? `"${pkg.path}"` : `"${pkg.name}"`;
|
|
221
|
+
const start = Math.max(0, lockfileContent.indexOf(needle));
|
|
222
|
+
const before = lockfileContent.slice(0, start).split('\n');
|
|
223
|
+
issues.push({
|
|
224
|
+
rule_id: adv.id,
|
|
225
|
+
rule_name: `${key}: ${adv.summary}`,
|
|
226
|
+
type: 'dependency',
|
|
227
|
+
severity: adv.severity,
|
|
228
|
+
action: 'alert',
|
|
229
|
+
match: key,
|
|
230
|
+
location: { start, end: start + needle.length, line: before.length, column: before[before.length - 1].length + 1 },
|
|
231
|
+
suggestion: adv.fixed
|
|
232
|
+
? `Upgrade ${pkg.name} to ${adv.fixed} or later. ${adv.url}`
|
|
233
|
+
: `No fixed version published yet. ${adv.url}`,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return issues;
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=osv.js.map
|
package/dist/osv.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osv.js","sourceRoot":"","sources":["../src/osv.ts"],"names":[],"mappings":"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"}
|
package/dist/sarif.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mund - The Guardian Protocol
|
|
3
|
+
* SARIF 2.1.0 output - for GitHub code scanning and other SARIF consumers
|
|
4
|
+
*
|
|
5
|
+
* https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
6
|
+
* GitHub-specific: rules carry `security-severity` (0.0-10.0) so code
|
|
7
|
+
* scanning ranks alerts as critical/high/medium/low.
|
|
8
|
+
*/
|
|
9
|
+
import type { SecurityIssue } from './types.js';
|
|
10
|
+
export interface SarifTarget {
|
|
11
|
+
/** Path relative to the repository root, using forward slashes. */
|
|
12
|
+
uri: string;
|
|
13
|
+
issues: SecurityIssue[];
|
|
14
|
+
}
|
|
15
|
+
export interface SarifOptions {
|
|
16
|
+
toolVersion: string;
|
|
17
|
+
informationUri?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function toSarif(targets: SarifTarget[], options: SarifOptions): object;
|
|
20
|
+
//# sourceMappingURL=sarif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,YAAY,CAAC;AAG1D,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAqBD,wBAAgB,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAyE7E"}
|
package/dist/sarif.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mund - The Guardian Protocol
|
|
3
|
+
* SARIF 2.1.0 output - for GitHub code scanning and other SARIF consumers
|
|
4
|
+
*
|
|
5
|
+
* https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
6
|
+
* GitHub-specific: rules carry `security-severity` (0.0-10.0) so code
|
|
7
|
+
* scanning ranks alerts as critical/high/medium/low.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from 'crypto';
|
|
10
|
+
import { computeRiskScore } from './scoring.js';
|
|
11
|
+
const LEVELS = {
|
|
12
|
+
critical: 'error',
|
|
13
|
+
high: 'error',
|
|
14
|
+
medium: 'warning',
|
|
15
|
+
low: 'note',
|
|
16
|
+
info: 'note',
|
|
17
|
+
};
|
|
18
|
+
/** GitHub maps >=9.0 critical, >=7.0 high, >=4.0 medium, >0.1 low. */
|
|
19
|
+
const SECURITY_SEVERITY = {
|
|
20
|
+
critical: '9.5',
|
|
21
|
+
high: '8.0',
|
|
22
|
+
medium: '5.5',
|
|
23
|
+
low: '3.0',
|
|
24
|
+
info: '0.0',
|
|
25
|
+
};
|
|
26
|
+
export function toSarif(targets, options) {
|
|
27
|
+
const rules = new Map();
|
|
28
|
+
const results = [];
|
|
29
|
+
const allIssues = [];
|
|
30
|
+
for (const target of targets) {
|
|
31
|
+
for (const issue of target.issues) {
|
|
32
|
+
allIssues.push(issue);
|
|
33
|
+
let rule = rules.get(issue.rule_id);
|
|
34
|
+
if (!rule) {
|
|
35
|
+
rule = { issue, index: rules.size };
|
|
36
|
+
rules.set(issue.rule_id, rule);
|
|
37
|
+
}
|
|
38
|
+
else if (severityRank(issue.severity) < severityRank(rule.issue.severity)) {
|
|
39
|
+
// A rule's descriptor takes the worst severity seen for it.
|
|
40
|
+
rule.issue = issue;
|
|
41
|
+
}
|
|
42
|
+
results.push({
|
|
43
|
+
ruleId: issue.rule_id,
|
|
44
|
+
ruleIndex: rule.index,
|
|
45
|
+
level: LEVELS[issue.severity] ?? 'warning',
|
|
46
|
+
message: { text: issue.suggestion ? `${issue.rule_name}. ${issue.suggestion}` : issue.rule_name },
|
|
47
|
+
locations: [{
|
|
48
|
+
physicalLocation: {
|
|
49
|
+
artifactLocation: { uri: target.uri },
|
|
50
|
+
...(issue.location?.line ? { region: toRegion(issue) } : {}),
|
|
51
|
+
},
|
|
52
|
+
}],
|
|
53
|
+
partialFingerprints: {
|
|
54
|
+
'mundFinding/v1': fingerprint(target.uri, issue),
|
|
55
|
+
},
|
|
56
|
+
properties: {
|
|
57
|
+
severity: issue.severity,
|
|
58
|
+
type: issue.type,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const risk = computeRiskScore(allIssues);
|
|
64
|
+
return {
|
|
65
|
+
$schema: 'https://json.schemastore.org/sarif-2.1.0.json',
|
|
66
|
+
version: '2.1.0',
|
|
67
|
+
runs: [{
|
|
68
|
+
tool: {
|
|
69
|
+
driver: {
|
|
70
|
+
name: 'Mund',
|
|
71
|
+
fullName: 'Mund - The Guardian Protocol',
|
|
72
|
+
version: options.toolVersion,
|
|
73
|
+
semanticVersion: options.toolVersion,
|
|
74
|
+
informationUri: options.informationUri ?? 'https://github.com/Tyox-all/Weave_Protocol/tree/main/mund',
|
|
75
|
+
rules: [...rules.values()].map(({ issue }) => ({
|
|
76
|
+
id: issue.rule_id,
|
|
77
|
+
name: toPascalCase(issue.rule_name),
|
|
78
|
+
shortDescription: { text: issue.rule_name },
|
|
79
|
+
...(issue.suggestion ? { help: { text: issue.suggestion } } : {}),
|
|
80
|
+
defaultConfiguration: { level: LEVELS[issue.severity] ?? 'warning' },
|
|
81
|
+
properties: {
|
|
82
|
+
'security-severity': SECURITY_SEVERITY[issue.severity] ?? '5.5',
|
|
83
|
+
tags: ['security', issue.type],
|
|
84
|
+
},
|
|
85
|
+
})),
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
results,
|
|
89
|
+
properties: {
|
|
90
|
+
riskScore: risk.score,
|
|
91
|
+
riskLabel: risk.label,
|
|
92
|
+
},
|
|
93
|
+
}],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function toRegion(issue) {
|
|
97
|
+
const loc = issue.location;
|
|
98
|
+
const region = { startLine: loc.line };
|
|
99
|
+
if (loc.column) {
|
|
100
|
+
region.startColumn = loc.column;
|
|
101
|
+
const length = loc.end - loc.start;
|
|
102
|
+
// Only emit endColumn for single-line matches; multi-line spans would
|
|
103
|
+
// need the source text to compute endLine.
|
|
104
|
+
if (length > 0 && !issue.match.includes('\n'))
|
|
105
|
+
region.endColumn = loc.column + length;
|
|
106
|
+
}
|
|
107
|
+
return region;
|
|
108
|
+
}
|
|
109
|
+
function fingerprint(uri, issue) {
|
|
110
|
+
return createHash('sha256')
|
|
111
|
+
.update(`${uri}\0${issue.rule_id}\0${issue.location?.line ?? 0}\0${issue.match}`)
|
|
112
|
+
.digest('hex')
|
|
113
|
+
.slice(0, 32);
|
|
114
|
+
}
|
|
115
|
+
function severityRank(severity) {
|
|
116
|
+
return ['critical', 'high', 'medium', 'low', 'info'].indexOf(severity);
|
|
117
|
+
}
|
|
118
|
+
function toPascalCase(text) {
|
|
119
|
+
return text.replace(/[^A-Za-z0-9]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
|
|
120
|
+
.replace(/^./, c => c.toUpperCase());
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mund - The Guardian Protocol
|
|
3
|
+
* Risk Scoring - collapse a set of findings into a 0-100 score + label
|
|
4
|
+
*
|
|
5
|
+
* Each finding independently "consumes" a fraction of the remaining
|
|
6
|
+
* headroom, so the score rises quickly with severe findings, has
|
|
7
|
+
* diminishing returns for many minor ones, and never exceeds 100:
|
|
8
|
+
*
|
|
9
|
+
* score = 100 * (1 - Π (1 - weight_i))
|
|
10
|
+
*
|
|
11
|
+
* One critical finding alone lands in the critical band; one high in the
|
|
12
|
+
* high band; it takes many low findings to escalate past "low".
|
|
13
|
+
*/
|
|
14
|
+
import type { SecurityIssue, Severity } from './types.js';
|
|
15
|
+
export type RiskLabel = 'none' | 'low' | 'medium' | 'high' | 'critical';
|
|
16
|
+
export interface RiskScore {
|
|
17
|
+
score: number;
|
|
18
|
+
label: RiskLabel;
|
|
19
|
+
counts: Record<Severity, number>;
|
|
20
|
+
}
|
|
21
|
+
export declare const SEVERITY_WEIGHTS: Record<Severity, number>;
|
|
22
|
+
/** Lower bound (inclusive) of each label band. */
|
|
23
|
+
export declare const RISK_BANDS: Array<[RiskLabel, number]>;
|
|
24
|
+
export declare function labelForScore(score: number): RiskLabel;
|
|
25
|
+
export declare function computeRiskScore(issues: Array<Pick<SecurityIssue, 'severity'>>): RiskScore;
|
|
26
|
+
//# sourceMappingURL=scoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAExE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAMrD,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAKjD,CAAC;AAEF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAKtD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,GAAG,SAAS,CAgB1F"}
|
package/dist/scoring.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mund - The Guardian Protocol
|
|
3
|
+
* Risk Scoring - collapse a set of findings into a 0-100 score + label
|
|
4
|
+
*
|
|
5
|
+
* Each finding independently "consumes" a fraction of the remaining
|
|
6
|
+
* headroom, so the score rises quickly with severe findings, has
|
|
7
|
+
* diminishing returns for many minor ones, and never exceeds 100:
|
|
8
|
+
*
|
|
9
|
+
* score = 100 * (1 - Π (1 - weight_i))
|
|
10
|
+
*
|
|
11
|
+
* One critical finding alone lands in the critical band; one high in the
|
|
12
|
+
* high band; it takes many low findings to escalate past "low".
|
|
13
|
+
*/
|
|
14
|
+
export const SEVERITY_WEIGHTS = {
|
|
15
|
+
critical: 0.8,
|
|
16
|
+
high: 0.4,
|
|
17
|
+
medium: 0.15,
|
|
18
|
+
low: 0.05,
|
|
19
|
+
info: 0,
|
|
20
|
+
};
|
|
21
|
+
/** Lower bound (inclusive) of each label band. */
|
|
22
|
+
export const RISK_BANDS = [
|
|
23
|
+
['critical', 80],
|
|
24
|
+
['high', 40],
|
|
25
|
+
['medium', 15],
|
|
26
|
+
['low', 1],
|
|
27
|
+
];
|
|
28
|
+
export function labelForScore(score) {
|
|
29
|
+
for (const [label, min] of RISK_BANDS) {
|
|
30
|
+
if (score >= min)
|
|
31
|
+
return label;
|
|
32
|
+
}
|
|
33
|
+
return 'none';
|
|
34
|
+
}
|
|
35
|
+
export function computeRiskScore(issues) {
|
|
36
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
37
|
+
let headroom = 1;
|
|
38
|
+
for (const issue of issues) {
|
|
39
|
+
const severity = issue.severity in SEVERITY_WEIGHTS ? issue.severity : 'info';
|
|
40
|
+
counts[severity]++;
|
|
41
|
+
headroom *= 1 - SEVERITY_WEIGHTS[severity];
|
|
42
|
+
}
|
|
43
|
+
// Any non-info finding scores at least 1 so it never reads as "none".
|
|
44
|
+
const raw = Math.round(100 * (1 - headroom));
|
|
45
|
+
const hasFinding = counts.critical + counts.high + counts.medium + counts.low > 0;
|
|
46
|
+
const score = hasFinding ? Math.max(1, raw) : 0;
|
|
47
|
+
return { score, label: labelForScore(score), counts };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH,MAAM,CAAC,MAAM,gBAAgB,GAA6B;IACxD,QAAQ,EAAE,GAAG;IACb,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,CAAC;CACR,CAAC;AAEF,kDAAkD;AAClD,MAAM,CAAC,MAAM,UAAU,GAA+B;IACpD,CAAC,UAAU,EAAE,EAAE,CAAC;IAChB,CAAC,MAAM,EAAE,EAAE,CAAC;IACZ,CAAC,QAAQ,EAAE,EAAE,CAAC;IACd,CAAC,KAAK,EAAE,CAAC,CAAC;CACX,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,UAAU,EAAE,CAAC;QACtC,IAAI,KAAK,IAAI,GAAG;YAAE,OAAO,KAAK,CAAC;IACjC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAA8C;IAC7E,MAAM,MAAM,GAA6B,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAC9F,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QAC9E,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnB,QAAQ,IAAI,CAAC,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAED,sEAAsE;IACtE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AACxD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"threat-intel-manager.d.ts","sourceRoot":"","sources":["../src/threat-intel-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,aAAa,EAEb,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,EACT,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"threat-intel-manager.d.ts","sourceRoot":"","sources":["../src/threat-intel-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,aAAa,EAEb,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,EACT,MAAM,yBAAyB,CAAC;AAqXjC,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,gBAAgB,CAAC,CAAO;;IAMhC,OAAO,CAAC,UAAU;IAmBlB,WAAW,CAAC,OAAO,CAAC,EAAE;QACpB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,QAAQ,CAAC,EAAE,aAAa,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,QAAQ,CAAC;KACxB,GAAG,aAAa,EAAE;IAwBnB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIjD,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAgBjF,UAAU,IAAI,WAAW,EAAE;IAI3B,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI9C,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAuB1E,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAyBxD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmLlF,gBAAgB,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwBzE,OAAO,CAAC,sBAAsB;IAQ9B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,gBAAgB;IA8FlE,SAAS,IAAI,WAAW;CAqFzB;AAMD,eAAO,MAAM,WAAW,oBAA2B,CAAC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Threat Intelligence Manager
|
|
3
3
|
* @weave_protocol/mund
|
|
4
4
|
*/
|
|
5
|
+
import { compileRulePattern } from './analyzers/regex.js';
|
|
5
6
|
// ============================================================================
|
|
6
7
|
// Built-in Threat Patterns
|
|
7
8
|
// ============================================================================
|
|
@@ -664,7 +665,7 @@ export class ThreatIntelManager {
|
|
|
664
665
|
continue;
|
|
665
666
|
// Check pattern
|
|
666
667
|
try {
|
|
667
|
-
const regex =
|
|
668
|
+
const regex = compileRulePattern(pattern.pattern, 'gi');
|
|
668
669
|
let match;
|
|
669
670
|
while ((match = regex.exec(content)) !== null) {
|
|
670
671
|
matches.push({
|