echelongraph-mcp 2.6.7 → 2.7.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/CHANGELOG.md +147 -0
- package/README.md +64 -30
- package/dist/http.js +2 -2
- package/dist/httpPolicy.js +13 -9
- package/dist/index.js +148 -57
- package/dist/prompts.js +102 -9
- package/dist/requiredText.js +118 -0
- package/dist/resources.js +2 -0
- package/dist/tools/check_affected.js +41 -18
- package/dist/tools/check_sbom.js +37 -374
- package/dist/tools/cve_intel.js +1 -1
- package/dist/tools/cve_remediation.js +257 -0
- package/dist/tools/epss_history.js +100 -6
- package/dist/tools/kev_recent.js +4 -4
- package/dist/tools/manifests/common.js +48 -0
- package/dist/tools/manifests/gomod.js +168 -0
- package/dist/tools/manifests/index.js +80 -0
- package/dist/tools/manifests/lockfiles.js +176 -0
- package/dist/tools/manifests/npmlock.js +107 -0
- package/dist/tools/manifests/requirements.js +125 -0
- package/dist/tools/manifests/toml.js +299 -0
- package/dist/tools/match_batch.js +432 -0
- package/dist/tools/scan_manifest.js +257 -0
- package/dist/tools/vendor_advisories.js +128 -20
- package/package.json +3 -1
- package/server.json +2 -2
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
// A TOML reader for the machine-written lockfiles scan_manifest reads (#2835): Cargo.lock and
|
|
2
|
+
// poetry.lock. No runtime dependency is added for it (the package ships with provenance and
|
|
3
|
+
// three dependencies, #2304), so it is this file: TOML 1.0 (toml.io/en/v1.0.0) as those tools
|
|
4
|
+
// write it.
|
|
5
|
+
//
|
|
6
|
+
// It reads comments; [table] and [[array.of.tables]] headers with bare, quoted and dotted keys;
|
|
7
|
+
// key = value with dotted keys; basic and literal strings, single- and multi-line, with escapes;
|
|
8
|
+
// integers, floats and booleans; dates and times (kept as their text); arrays across lines with
|
|
9
|
+
// comments and a trailing comma; inline tables. What it does not hold to the spec: it does not
|
|
10
|
+
// refuse every redefinition TOML forbids, which a lockfile writer does not produce. Anything it
|
|
11
|
+
// cannot read throws Unreadable with the line, and the file is reported as not read, never as
|
|
12
|
+
// holding no dependencies.
|
|
13
|
+
//
|
|
14
|
+
// Each table's line (1-based, its header's) is kept in `lines`, so an entry not checked can say
|
|
15
|
+
// where it is.
|
|
16
|
+
import { Unreadable } from "./common.js";
|
|
17
|
+
export function readToml(text) {
|
|
18
|
+
const s = text.replace(/\r\n/g, "\n");
|
|
19
|
+
let i = 0;
|
|
20
|
+
let line = 1;
|
|
21
|
+
const lines = new WeakMap();
|
|
22
|
+
const root = {};
|
|
23
|
+
lines.set(root, 1);
|
|
24
|
+
let current = root;
|
|
25
|
+
const fail = (why) => {
|
|
26
|
+
throw new Unreadable(`is not TOML this reader can read: ${why} on line ${line}`);
|
|
27
|
+
};
|
|
28
|
+
const peek = (n = 0) => s[i + n];
|
|
29
|
+
const advance = (n = 1) => {
|
|
30
|
+
for (let k = 0; k < n; k++)
|
|
31
|
+
if (s[i++] === "\n")
|
|
32
|
+
line++;
|
|
33
|
+
};
|
|
34
|
+
const skipWs = () => {
|
|
35
|
+
while (peek() === " " || peek() === "\t")
|
|
36
|
+
advance();
|
|
37
|
+
};
|
|
38
|
+
const skipComment = () => {
|
|
39
|
+
if (peek() === "#")
|
|
40
|
+
while (i < s.length && peek() !== "\n")
|
|
41
|
+
advance();
|
|
42
|
+
};
|
|
43
|
+
// Whitespace, newlines and comments (inside an array).
|
|
44
|
+
const skipAll = () => {
|
|
45
|
+
for (;;) {
|
|
46
|
+
skipWs();
|
|
47
|
+
skipComment();
|
|
48
|
+
if (peek() === "\n")
|
|
49
|
+
advance();
|
|
50
|
+
else
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const endOfLine = () => {
|
|
55
|
+
skipWs();
|
|
56
|
+
skipComment();
|
|
57
|
+
if (i < s.length && peek() !== "\n")
|
|
58
|
+
fail(`unexpected "${peek()}"`);
|
|
59
|
+
};
|
|
60
|
+
const ESC = { b: "\b", t: "\t", n: "\n", f: "\f", r: "\r", '"': '"', "\\": "\\", e: "\x1b" };
|
|
61
|
+
const basic = (multi) => {
|
|
62
|
+
let out = "";
|
|
63
|
+
advance(multi ? 3 : 1);
|
|
64
|
+
if (multi && peek() === "\n")
|
|
65
|
+
advance();
|
|
66
|
+
for (;;) {
|
|
67
|
+
if (i >= s.length)
|
|
68
|
+
fail("an unterminated string");
|
|
69
|
+
// A closing """ may follow up to two quotes of the string's own: """" ends it with one.
|
|
70
|
+
if (multi ? s.startsWith('"""', i) && s[i + 3] !== '"' : peek() === '"') {
|
|
71
|
+
advance(multi ? 3 : 1);
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
const c = peek();
|
|
75
|
+
if (!multi && c === "\n")
|
|
76
|
+
fail("a newline in a string");
|
|
77
|
+
if (c === "\\") {
|
|
78
|
+
const n = peek(1);
|
|
79
|
+
if (multi && (n === "\n" || n === " " || n === "\t")) {
|
|
80
|
+
// A line-ending backslash: the newline and the whitespace after it are dropped.
|
|
81
|
+
advance();
|
|
82
|
+
while (peek() === " " || peek() === "\t" || peek() === "\n")
|
|
83
|
+
advance();
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (n === "u" || n === "U") {
|
|
87
|
+
const len = n === "u" ? 4 : 8;
|
|
88
|
+
const hex = s.slice(i + 2, i + 2 + len);
|
|
89
|
+
if (!/^[0-9a-fA-F]+$/.test(hex) || hex.length !== len)
|
|
90
|
+
fail("a bad unicode escape");
|
|
91
|
+
out += String.fromCodePoint(parseInt(hex, 16));
|
|
92
|
+
advance(2 + len);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (ESC[n] === undefined)
|
|
96
|
+
fail(`an unknown escape \\${n}`);
|
|
97
|
+
out += ESC[n];
|
|
98
|
+
advance(2);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
out += c;
|
|
102
|
+
advance();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const literal = (multi) => {
|
|
106
|
+
advance(multi ? 3 : 1);
|
|
107
|
+
if (multi && peek() === "\n")
|
|
108
|
+
advance();
|
|
109
|
+
const close = multi ? "'''" : "'";
|
|
110
|
+
let end = s.indexOf(close, i);
|
|
111
|
+
if (multi)
|
|
112
|
+
while (end >= 0 && s[end + 3] === "'")
|
|
113
|
+
end++;
|
|
114
|
+
if (end < 0)
|
|
115
|
+
fail("an unterminated string");
|
|
116
|
+
const out = s.slice(i, end);
|
|
117
|
+
if (!multi && out.includes("\n"))
|
|
118
|
+
fail("a newline in a string");
|
|
119
|
+
advance(end - i + close.length);
|
|
120
|
+
return out;
|
|
121
|
+
};
|
|
122
|
+
const key = () => {
|
|
123
|
+
skipWs();
|
|
124
|
+
const c = peek();
|
|
125
|
+
if (c === '"')
|
|
126
|
+
return basic(false);
|
|
127
|
+
if (c === "'")
|
|
128
|
+
return literal(false);
|
|
129
|
+
const m = /^[A-Za-z0-9_-]+/.exec(s.slice(i, i + 256));
|
|
130
|
+
if (!m)
|
|
131
|
+
fail("a missing key");
|
|
132
|
+
advance(m[0].length);
|
|
133
|
+
return m[0];
|
|
134
|
+
};
|
|
135
|
+
const dottedKey = () => {
|
|
136
|
+
const parts = [key()];
|
|
137
|
+
for (;;) {
|
|
138
|
+
skipWs();
|
|
139
|
+
if (peek() !== ".")
|
|
140
|
+
return parts;
|
|
141
|
+
advance();
|
|
142
|
+
parts.push(key());
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const value = () => {
|
|
146
|
+
skipWs();
|
|
147
|
+
const c = peek();
|
|
148
|
+
if (s.startsWith('"""', i))
|
|
149
|
+
return basic(true);
|
|
150
|
+
if (c === '"')
|
|
151
|
+
return basic(false);
|
|
152
|
+
if (s.startsWith("'''", i))
|
|
153
|
+
return literal(true);
|
|
154
|
+
if (c === "'")
|
|
155
|
+
return literal(false);
|
|
156
|
+
if (c === "[") {
|
|
157
|
+
advance();
|
|
158
|
+
const arr = [];
|
|
159
|
+
for (;;) {
|
|
160
|
+
skipAll();
|
|
161
|
+
if (peek() === "]") {
|
|
162
|
+
advance();
|
|
163
|
+
return arr;
|
|
164
|
+
}
|
|
165
|
+
arr.push(value());
|
|
166
|
+
skipAll();
|
|
167
|
+
if (peek() === ",")
|
|
168
|
+
advance();
|
|
169
|
+
else if (peek() !== "]")
|
|
170
|
+
fail("a missing , in an array");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (c === "{") {
|
|
174
|
+
advance();
|
|
175
|
+
const t = {};
|
|
176
|
+
lines.set(t, line);
|
|
177
|
+
skipWs();
|
|
178
|
+
if (peek() === "}") {
|
|
179
|
+
advance();
|
|
180
|
+
return t;
|
|
181
|
+
}
|
|
182
|
+
for (;;) {
|
|
183
|
+
const k = dottedKey();
|
|
184
|
+
skipWs();
|
|
185
|
+
if (peek() !== "=")
|
|
186
|
+
fail("a missing = in an inline table");
|
|
187
|
+
advance();
|
|
188
|
+
setPath(t, k, value());
|
|
189
|
+
skipWs();
|
|
190
|
+
if (peek() === ",") {
|
|
191
|
+
advance();
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (peek() === "}") {
|
|
195
|
+
advance();
|
|
196
|
+
return t;
|
|
197
|
+
}
|
|
198
|
+
fail("a missing , in an inline table");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// A bare scalar: up to a delimiter.
|
|
202
|
+
const m = /^[^\s,\]}#]+(?: [0-9:.+\-Z]+)?/.exec(s.slice(i, i + 128));
|
|
203
|
+
if (!m)
|
|
204
|
+
fail("a missing value");
|
|
205
|
+
const raw = m[0];
|
|
206
|
+
advance(raw.length);
|
|
207
|
+
if (raw === "true")
|
|
208
|
+
return true;
|
|
209
|
+
if (raw === "false")
|
|
210
|
+
return false;
|
|
211
|
+
const num = raw.replace(/_/g, "");
|
|
212
|
+
if (/^[+-]?(0x[0-9a-fA-F]+|0o[0-7]+|0b[01]+|\d+)$/.test(num))
|
|
213
|
+
return Number(num.replace(/^\+/, ""));
|
|
214
|
+
if (/^[+-]?(\d+(\.\d+)?([eE][+-]?\d+)?|inf|nan)$/.test(num))
|
|
215
|
+
return Number(num.replace(/^\+/, ""));
|
|
216
|
+
if (/^\d{4}-\d{2}-\d{2}|^\d{2}:\d{2}/.test(raw))
|
|
217
|
+
return raw;
|
|
218
|
+
return fail(`an unreadable value "${raw.slice(0, 40)}"`);
|
|
219
|
+
};
|
|
220
|
+
const setPath = (t, path, v) => {
|
|
221
|
+
let at = t;
|
|
222
|
+
for (const k of path.slice(0, -1)) {
|
|
223
|
+
if (at[k] === undefined) {
|
|
224
|
+
at[k] = {};
|
|
225
|
+
lines.set(at[k], line);
|
|
226
|
+
}
|
|
227
|
+
const next = at[k];
|
|
228
|
+
if (typeof next !== "object" || Array.isArray(next))
|
|
229
|
+
fail(`the key ${k} is not a table`);
|
|
230
|
+
at = next;
|
|
231
|
+
}
|
|
232
|
+
at[path[path.length - 1]] = v;
|
|
233
|
+
};
|
|
234
|
+
// The table a header names, made as needed; [[x]] appends a new table to the array x.
|
|
235
|
+
const header = (path, array) => {
|
|
236
|
+
let at = root;
|
|
237
|
+
path.forEach((k, n) => {
|
|
238
|
+
const last = n === path.length - 1;
|
|
239
|
+
if (last && array) {
|
|
240
|
+
if (at[k] === undefined)
|
|
241
|
+
at[k] = [];
|
|
242
|
+
if (!Array.isArray(at[k]))
|
|
243
|
+
fail(`[[${path.join(".")}]] names a key that is not an array of tables`);
|
|
244
|
+
const t = {};
|
|
245
|
+
lines.set(t, line);
|
|
246
|
+
at[k].push(t);
|
|
247
|
+
at = t;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
let next = at[k];
|
|
251
|
+
if (next === undefined) {
|
|
252
|
+
next = {};
|
|
253
|
+
lines.set(next, line);
|
|
254
|
+
at[k] = next;
|
|
255
|
+
}
|
|
256
|
+
// A dotted header through an array of tables goes into its last table.
|
|
257
|
+
if (Array.isArray(next))
|
|
258
|
+
next = next[next.length - 1];
|
|
259
|
+
if (typeof next !== "object" || next === null || Array.isArray(next))
|
|
260
|
+
fail(`[${path.join(".")}] names a key that is not a table`);
|
|
261
|
+
at = next;
|
|
262
|
+
});
|
|
263
|
+
return at;
|
|
264
|
+
};
|
|
265
|
+
while (i < s.length) {
|
|
266
|
+
skipWs();
|
|
267
|
+
const c = peek();
|
|
268
|
+
if (c === "\n") {
|
|
269
|
+
advance();
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (c === "#") {
|
|
273
|
+
skipComment();
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (i >= s.length)
|
|
277
|
+
break;
|
|
278
|
+
if (c === "[") {
|
|
279
|
+
const array = peek(1) === "[";
|
|
280
|
+
advance(array ? 2 : 1);
|
|
281
|
+
const path = dottedKey();
|
|
282
|
+
skipWs();
|
|
283
|
+
if (array ? !s.startsWith("]]", i) : peek() !== "]")
|
|
284
|
+
fail("an unclosed table header");
|
|
285
|
+
advance(array ? 2 : 1);
|
|
286
|
+
current = header(path, array);
|
|
287
|
+
endOfLine();
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const k = dottedKey();
|
|
291
|
+
skipWs();
|
|
292
|
+
if (peek() !== "=")
|
|
293
|
+
fail("a missing =");
|
|
294
|
+
advance();
|
|
295
|
+
setPath(current, k, value());
|
|
296
|
+
endOfLine();
|
|
297
|
+
}
|
|
298
|
+
return { root, lines };
|
|
299
|
+
}
|