@node9/proxy 2.8.4 → 2.9.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/dist/cli.js +3775 -1865
- package/dist/cli.mjs +3711 -1801
- package/dist/dashboard.mjs +854 -165
- package/dist/index.js +972 -113
- package/dist/index.mjs +968 -109
- package/dist/scan-ink.mjs +213 -99
- package/package.json +1 -1
package/dist/scan-ink.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/cli/render/ink/StaticScorecard.tsx
|
|
2
|
-
import { Box as
|
|
2
|
+
import { Box as Box12, render } from "ink";
|
|
3
3
|
|
|
4
4
|
// src/cli/render/ink/SeverityBand.tsx
|
|
5
5
|
import { Box, Text } from "ink";
|
|
@@ -177,7 +177,11 @@ var ConfigFileSchema = z.object({
|
|
|
177
177
|
mode: z.enum(["off", "review", "block"]).optional(),
|
|
178
178
|
allow: z.array(z.string()).optional(),
|
|
179
179
|
deny: z.array(z.string()).optional(),
|
|
180
|
-
allowPrivate: z.boolean().optional()
|
|
180
|
+
allowPrivate: z.boolean().optional(),
|
|
181
|
+
// SSRF floor. `ssrfAllow` exempts OVERRIDABLE tiers only; a tier-1
|
|
182
|
+
// entry is dropped with a warning at load, never silently honoured.
|
|
183
|
+
ssrfAllow: z.array(z.string()).optional(),
|
|
184
|
+
ssrfStrict: z.boolean().optional()
|
|
181
185
|
}).optional(),
|
|
182
186
|
loopDetection: z.object({
|
|
183
187
|
enabled: z.boolean().optional(),
|
|
@@ -205,10 +209,52 @@ import os from "os";
|
|
|
205
209
|
|
|
206
210
|
// packages/policy-engine/dist/index.mjs
|
|
207
211
|
import safeRegex from "safe-regex2";
|
|
212
|
+
import { createHash } from "crypto";
|
|
208
213
|
import mvdanSh from "mvdan-sh";
|
|
209
214
|
import pm from "picomatch";
|
|
210
215
|
import safeRegex2 from "safe-regex2";
|
|
211
216
|
import safeRegex3 from "safe-regex2";
|
|
217
|
+
var B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
218
|
+
var B58_INDEX = Object.fromEntries(
|
|
219
|
+
[...B58].map((c, i) => [c, i])
|
|
220
|
+
);
|
|
221
|
+
function validateBase58Check(s) {
|
|
222
|
+
if (!s) return null;
|
|
223
|
+
let n = 0n;
|
|
224
|
+
for (const c of s) {
|
|
225
|
+
const v = B58_INDEX[c];
|
|
226
|
+
if (v === void 0) return null;
|
|
227
|
+
n = n * 58n + BigInt(v);
|
|
228
|
+
}
|
|
229
|
+
let hex = n.toString(16);
|
|
230
|
+
if (hex.length % 2) hex = "0" + hex;
|
|
231
|
+
let zeros = 0;
|
|
232
|
+
for (const c of s) {
|
|
233
|
+
if (c !== "1") break;
|
|
234
|
+
zeros++;
|
|
235
|
+
}
|
|
236
|
+
const bytes = Buffer.concat([
|
|
237
|
+
Buffer.alloc(zeros),
|
|
238
|
+
n === 0n ? Buffer.alloc(0) : Buffer.from(hex, "hex")
|
|
239
|
+
]);
|
|
240
|
+
if (bytes.length < 5) return null;
|
|
241
|
+
const body = bytes.subarray(0, bytes.length - 4);
|
|
242
|
+
const check = bytes.subarray(bytes.length - 4);
|
|
243
|
+
const h = createHash("sha256").update(createHash("sha256").update(body).digest()).digest();
|
|
244
|
+
return h.subarray(0, 4).equals(check) ? body : null;
|
|
245
|
+
}
|
|
246
|
+
function validateWif(s) {
|
|
247
|
+
const p = validateBase58Check(s);
|
|
248
|
+
if (!p || p[0] !== 128) return false;
|
|
249
|
+
return p.length === 33 || p.length === 34 && p[33] === 1;
|
|
250
|
+
}
|
|
251
|
+
var XPRV_VERSIONS = /* @__PURE__ */ new Set([76066276, 77428856, 78791436]);
|
|
252
|
+
function validateXprv(s) {
|
|
253
|
+
const p = validateBase58Check(s);
|
|
254
|
+
if (!p || p.length !== 78) return false;
|
|
255
|
+
const version = p.readUInt32BE(0);
|
|
256
|
+
return XPRV_VERSIONS.has(version) && p[45] === 0;
|
|
257
|
+
}
|
|
212
258
|
var DLP_PATTERNS = [
|
|
213
259
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
214
260
|
{
|
|
@@ -360,6 +406,27 @@ var DLP_PATTERNS = [
|
|
|
360
406
|
severity: "block",
|
|
361
407
|
keywords: ["sg."]
|
|
362
408
|
},
|
|
409
|
+
// ── Cryptocurrency private keys (base58check-validated) ───────────────────
|
|
410
|
+
// Both are anchored with \b on each side: unanchored, `[KL][base58]{51}`
|
|
411
|
+
// matches INSIDE any longer base58 blob (an xprv, a Solana keypair, a
|
|
412
|
+
// Monero address). Lookbehind fails safe-regex2; \b is the house style
|
|
413
|
+
// (see the card regexes). Mainnet only, matching validateWif / validateXprv;
|
|
414
|
+
// testnet (WIF 0xEF, tprv) is deferred. Cost was measured: the WIF regex
|
|
415
|
+
// runs on every string (first keyword-less pattern) at 0.024 ms per 100 KB
|
|
416
|
+
// of prose, so no prefilter is warranted.
|
|
417
|
+
{
|
|
418
|
+
name: "Bitcoin WIF Private Key",
|
|
419
|
+
regex: /\b(?:5[1-9A-HJ-NP-Za-km-z]{50}|[KL][1-9A-HJ-NP-Za-km-z]{51})\b/,
|
|
420
|
+
severity: "block",
|
|
421
|
+
validate: validateWif
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: "Extended Private Key",
|
|
425
|
+
regex: /\b[xyz]prv[1-9A-HJ-NP-Za-km-z]{107}\b/,
|
|
426
|
+
severity: "block",
|
|
427
|
+
keywords: ["xprv", "yprv", "zprv"],
|
|
428
|
+
validate: validateXprv
|
|
429
|
+
},
|
|
363
430
|
// ── Private keys (PEM) ────────────────────────────────────────────────────
|
|
364
431
|
{
|
|
365
432
|
name: "Private Key (PEM)",
|
|
@@ -1743,10 +1810,52 @@ function LeaksPanel({ summary, width, now = /* @__PURE__ */ new Date() }) {
|
|
|
1743
1810
|
] });
|
|
1744
1811
|
}
|
|
1745
1812
|
|
|
1746
|
-
// src/cli/render/ink/panels/
|
|
1813
|
+
// src/cli/render/ink/panels/DecoyPanel.tsx
|
|
1747
1814
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
1815
|
+
import os2 from "os";
|
|
1748
1816
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1749
|
-
var ROW_LIMIT2 =
|
|
1817
|
+
var ROW_LIMIT2 = 4;
|
|
1818
|
+
function shortPath(p) {
|
|
1819
|
+
const home = os2.homedir();
|
|
1820
|
+
return p.startsWith(home) ? "~" + p.slice(home.length) : p;
|
|
1821
|
+
}
|
|
1822
|
+
function decoySeamPhrase(toolName) {
|
|
1823
|
+
if (toolName === "tool-result") return "read into the agent context";
|
|
1824
|
+
if (toolName === "user-prompt") return "pasted into a prompt";
|
|
1825
|
+
return `appeared in a ${toolName} call`;
|
|
1826
|
+
}
|
|
1827
|
+
function DecoyPanel({ summary, width, now = /* @__PURE__ */ new Date() }) {
|
|
1828
|
+
const rows = summary.canaries;
|
|
1829
|
+
if (rows.length === 0) return null;
|
|
1830
|
+
return /* @__PURE__ */ jsxs6(Box6, { borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", width, children: [
|
|
1831
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: true, color: "red", children: [
|
|
1832
|
+
"DECOY TRIPPED",
|
|
1833
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: topOf(Math.min(rows.length, ROW_LIMIT2), rows.length) })
|
|
1834
|
+
] }),
|
|
1835
|
+
rows.slice(0, ROW_LIMIT2).map((c, i) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1836
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1837
|
+
/* @__PURE__ */ jsx6(Box6, { width: 5, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: relativeDate(c.timestamp, now).padStart(4) }) }),
|
|
1838
|
+
/* @__PURE__ */ jsx6(Box6, { width: 16, children: /* @__PURE__ */ jsxs6(Text6, { color: "red", bold: true, wrap: "truncate-end", children: [
|
|
1839
|
+
c.kind,
|
|
1840
|
+
c.retired ? " (retired)" : ""
|
|
1841
|
+
] }) }),
|
|
1842
|
+
/* @__PURE__ */ jsx6(Box6, { width: 15, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate-end", children: `[${c.toolName}]` }) }),
|
|
1843
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate-end", children: c.agent })
|
|
1844
|
+
] }),
|
|
1845
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate-end", children: ` ${shortPath(c.path)} \u2014 ${decoySeamPhrase(c.toolName)}` })
|
|
1846
|
+
] }, i)),
|
|
1847
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1848
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2192 " }),
|
|
1849
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "node9 canary status" }),
|
|
1850
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate-end", children: " (something read that file)" })
|
|
1851
|
+
] })
|
|
1852
|
+
] });
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// src/cli/render/ink/panels/BlockedPanel.tsx
|
|
1856
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
1857
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1858
|
+
var ROW_LIMIT3 = 12;
|
|
1750
1859
|
function BlockedPanel({
|
|
1751
1860
|
summary,
|
|
1752
1861
|
width,
|
|
@@ -1754,30 +1863,30 @@ function BlockedPanel({
|
|
|
1754
1863
|
}) {
|
|
1755
1864
|
const enabled = new Set(enabledShields);
|
|
1756
1865
|
const allRules = topRulesByVerdict(summary.sections, "block", Number.MAX_SAFE_INTEGER);
|
|
1757
|
-
const rules = allRules.slice(0,
|
|
1866
|
+
const rules = allRules.slice(0, ROW_LIMIT3);
|
|
1758
1867
|
if (rules.length === 0) return null;
|
|
1759
1868
|
const origins = rules.map((r) => originForRule(r.name, summary.sections, enabled));
|
|
1760
1869
|
const anyNeeds = origins.some((o) => o.startsWith("needs shield:"));
|
|
1761
1870
|
const footer = enabled.size === 0 ? "\u2192 install node9 + enable shields above" : anyNeeds ? "\u2192 \u2713 marks rules from enabled shields \xB7 enable the rest above" : "\u2192 \u2713 all these rules come from enabled shields";
|
|
1762
|
-
return /* @__PURE__ */
|
|
1763
|
-
/* @__PURE__ */
|
|
1871
|
+
return /* @__PURE__ */ jsxs7(Box7, { borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", width, children: [
|
|
1872
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: "red", children: [
|
|
1764
1873
|
"WOULD HAVE BLOCKED",
|
|
1765
|
-
/* @__PURE__ */
|
|
1874
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: topOf(rules.length, allRules.length) })
|
|
1766
1875
|
] }),
|
|
1767
|
-
rules.map((rule, i) => /* @__PURE__ */
|
|
1768
|
-
/* @__PURE__ */
|
|
1769
|
-
/* @__PURE__ */
|
|
1770
|
-
/* @__PURE__ */
|
|
1771
|
-
/* @__PURE__ */
|
|
1876
|
+
rules.map((rule, i) => /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1877
|
+
/* @__PURE__ */ jsx7(Box7, { width: 3, children: /* @__PURE__ */ jsx7(Text7, { color: "red", children: "\u2717" }) }),
|
|
1878
|
+
/* @__PURE__ */ jsx7(Box7, { width: 24, children: /* @__PURE__ */ jsx7(Text7, { bold: true, wrap: "truncate-end", children: rule.name }) }),
|
|
1879
|
+
/* @__PURE__ */ jsx7(Box7, { width: 6, children: /* @__PURE__ */ jsx7(Text7, { bold: true, children: `\xD7${rule.count}` }) }),
|
|
1880
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate-end", children: originForRule(rule.name, summary.sections, enabled) })
|
|
1772
1881
|
] }, i)),
|
|
1773
|
-
/* @__PURE__ */
|
|
1882
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate-end", children: footer }) })
|
|
1774
1883
|
] });
|
|
1775
1884
|
}
|
|
1776
1885
|
|
|
1777
1886
|
// src/cli/render/ink/panels/BlastRadiusPanel.tsx
|
|
1778
|
-
import { Box as
|
|
1779
|
-
import { jsx as
|
|
1780
|
-
var
|
|
1887
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
1888
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1889
|
+
var ROW_LIMIT4 = 4;
|
|
1781
1890
|
var ENV_ROW_LIMIT = 3;
|
|
1782
1891
|
function BlastRadiusPanel({
|
|
1783
1892
|
blast,
|
|
@@ -1785,62 +1894,62 @@ function BlastRadiusPanel({
|
|
|
1785
1894
|
width
|
|
1786
1895
|
}) {
|
|
1787
1896
|
if (blastExposures === 0) return null;
|
|
1788
|
-
const shown = Math.min(blast.reachable.length,
|
|
1789
|
-
return /* @__PURE__ */
|
|
1790
|
-
/* @__PURE__ */
|
|
1897
|
+
const shown = Math.min(blast.reachable.length, ROW_LIMIT4) + Math.min(blast.envFindings.length, ENV_ROW_LIMIT);
|
|
1898
|
+
return /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "yellow", paddingX: 1, flexDirection: "column", width, children: [
|
|
1899
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: "yellow", children: [
|
|
1791
1900
|
"BLAST RADIUS",
|
|
1792
|
-
/* @__PURE__ */
|
|
1901
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: topOf(shown, blastExposures) })
|
|
1793
1902
|
] }),
|
|
1794
|
-
blast.reachable.slice(0,
|
|
1903
|
+
blast.reachable.slice(0, ROW_LIMIT4).map((path2, i) => {
|
|
1795
1904
|
const desc = path2.description.split(" \u2014 ")[0].split(/—|--/)[0].trim();
|
|
1796
|
-
return /* @__PURE__ */
|
|
1797
|
-
/* @__PURE__ */
|
|
1798
|
-
/* @__PURE__ */
|
|
1799
|
-
/* @__PURE__ */
|
|
1905
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1906
|
+
/* @__PURE__ */ jsx8(Box8, { width: 3, children: /* @__PURE__ */ jsx8(Text8, { color: "red", children: "\u2717" }) }),
|
|
1907
|
+
/* @__PURE__ */ jsx8(Box8, { width: 36, children: /* @__PURE__ */ jsx8(Text8, { children: path2.label }) }),
|
|
1908
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: desc })
|
|
1800
1909
|
] }, i);
|
|
1801
1910
|
}),
|
|
1802
|
-
blast.envFindings.slice(0, 3).map((env, i) => /* @__PURE__ */
|
|
1803
|
-
/* @__PURE__ */
|
|
1804
|
-
/* @__PURE__ */
|
|
1805
|
-
/* @__PURE__ */
|
|
1911
|
+
blast.envFindings.slice(0, 3).map((env, i) => /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1912
|
+
/* @__PURE__ */ jsx8(Box8, { width: 3, children: /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u26A0" }) }),
|
|
1913
|
+
/* @__PURE__ */ jsx8(Text8, { children: env.key }),
|
|
1914
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: ` (${env.patternName})` })
|
|
1806
1915
|
] }, `env-${i}`)),
|
|
1807
|
-
/* @__PURE__ */
|
|
1808
|
-
/* @__PURE__ */
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1917
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "\u2192 " }),
|
|
1918
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: "cyan", children: "project-jail" }),
|
|
1919
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " shield blocks agent reads of these paths" })
|
|
1811
1920
|
] })
|
|
1812
1921
|
] });
|
|
1813
1922
|
}
|
|
1814
1923
|
|
|
1815
1924
|
// src/cli/render/ink/panels/ReviewQueuePanel.tsx
|
|
1816
|
-
import { Box as
|
|
1817
|
-
import { jsx as
|
|
1818
|
-
var
|
|
1925
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
1926
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1927
|
+
var ROW_LIMIT5 = 5;
|
|
1819
1928
|
function ReviewQueuePanel({
|
|
1820
1929
|
summary,
|
|
1821
1930
|
width,
|
|
1822
1931
|
enabledShields = []
|
|
1823
1932
|
}) {
|
|
1824
1933
|
const enabled = new Set(enabledShields);
|
|
1825
|
-
const rules = topRulesByVerdict(summary.sections, "review",
|
|
1934
|
+
const rules = topRulesByVerdict(summary.sections, "review", ROW_LIMIT5);
|
|
1826
1935
|
if (rules.length === 0) return null;
|
|
1827
|
-
return /* @__PURE__ */
|
|
1828
|
-
/* @__PURE__ */
|
|
1829
|
-
rules.map((rule, i) => /* @__PURE__ */
|
|
1830
|
-
/* @__PURE__ */
|
|
1831
|
-
/* @__PURE__ */
|
|
1832
|
-
/* @__PURE__ */
|
|
1936
|
+
return /* @__PURE__ */ jsxs9(Box9, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", width, children: [
|
|
1937
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "REVIEW QUEUE" }),
|
|
1938
|
+
rules.map((rule, i) => /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
1939
|
+
/* @__PURE__ */ jsx9(Box9, { width: 22, children: /* @__PURE__ */ jsx9(Text9, { wrap: "truncate-end", children: rule.name }) }),
|
|
1940
|
+
/* @__PURE__ */ jsx9(Box9, { width: 6, children: /* @__PURE__ */ jsx9(Text9, { bold: true, children: `\xD7${rule.count}` }) }),
|
|
1941
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, wrap: "truncate-end", children: originForRule(rule.name, summary.sections, enabled) })
|
|
1833
1942
|
] }, i)),
|
|
1834
|
-
/* @__PURE__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
1836
|
-
/* @__PURE__ */
|
|
1943
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
1944
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, wrap: "truncate-end", children: "\u2192 " }),
|
|
1945
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: "cyan", wrap: "truncate-end", children: "runtime approval" })
|
|
1837
1946
|
] })
|
|
1838
1947
|
] });
|
|
1839
1948
|
}
|
|
1840
1949
|
|
|
1841
1950
|
// src/cli/render/ink/panels/AgentLoopsPanel.tsx
|
|
1842
|
-
import { Box as
|
|
1843
|
-
import { Fragment, jsx as
|
|
1951
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1952
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1844
1953
|
var TOOL_ROW_LIMIT = 5;
|
|
1845
1954
|
var STUCK_ROW_LIMIT = 1;
|
|
1846
1955
|
function fmtNum2(n) {
|
|
@@ -1861,38 +1970,38 @@ function AgentLoopsPanel({ loopFindings, width }) {
|
|
|
1861
1970
|
}
|
|
1862
1971
|
const toolEntries = [...byTool.entries()].sort((a, b) => b[1] - a[1]).slice(0, TOOL_ROW_LIMIT);
|
|
1863
1972
|
const topStuck = [...loopFindings].sort((a, b) => b.count - a.count).slice(0, STUCK_ROW_LIMIT);
|
|
1864
|
-
return /* @__PURE__ */
|
|
1865
|
-
/* @__PURE__ */
|
|
1973
|
+
return /* @__PURE__ */ jsxs10(Box10, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", width, children: [
|
|
1974
|
+
/* @__PURE__ */ jsxs10(Text10, { bold: true, children: [
|
|
1866
1975
|
"AGENT LOOPS",
|
|
1867
|
-
/* @__PURE__ */
|
|
1976
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: topOf(toolEntries.length, byTool.size) })
|
|
1868
1977
|
] }),
|
|
1869
1978
|
toolEntries.map(([tool, repeats]) => {
|
|
1870
1979
|
const pct = totalRepeats > 0 ? Math.round(repeats / totalRepeats * 100) : 0;
|
|
1871
|
-
return /* @__PURE__ */
|
|
1872
|
-
/* @__PURE__ */
|
|
1873
|
-
/* @__PURE__ */
|
|
1874
|
-
/* @__PURE__ */
|
|
1980
|
+
return /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
1981
|
+
/* @__PURE__ */ jsx10(Box10, { width: 10, children: /* @__PURE__ */ jsx10(Text10, { bold: true, children: tool }) }),
|
|
1982
|
+
/* @__PURE__ */ jsx10(Box10, { width: 14, children: /* @__PURE__ */ jsx10(Text10, { children: `\xD7${fmtNum2(repeats)}` }) }),
|
|
1983
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: `${pct}%` })
|
|
1875
1984
|
] }, tool);
|
|
1876
1985
|
}),
|
|
1877
|
-
topStuck.length > 0 ? /* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
1986
|
+
topStuck.length > 0 ? /* @__PURE__ */ jsxs10(Fragment, { children: [
|
|
1987
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Top stuck:" }),
|
|
1879
1988
|
topStuck.map((f, i) => {
|
|
1880
1989
|
const target = trimRight(f.commandPreview || f.toolName, 32);
|
|
1881
|
-
return /* @__PURE__ */
|
|
1882
|
-
/* @__PURE__ */
|
|
1883
|
-
/* @__PURE__ */
|
|
1990
|
+
return /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
1991
|
+
/* @__PURE__ */ jsx10(Box10, { width: 8, children: /* @__PURE__ */ jsx10(Text10, { bold: true, children: `\xD7${fmtNum2(f.count)}` }) }),
|
|
1992
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "truncate-end", children: target })
|
|
1884
1993
|
] }, `stuck-${i}`);
|
|
1885
1994
|
})
|
|
1886
1995
|
] }) : null,
|
|
1887
|
-
/* @__PURE__ */
|
|
1888
|
-
/* @__PURE__ */
|
|
1889
|
-
/* @__PURE__ */
|
|
1996
|
+
/* @__PURE__ */ jsxs10(Box10, { children: [
|
|
1997
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "truncate-end", children: "\u2192 " }),
|
|
1998
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", wrap: "truncate-end", children: "live loop-detector" })
|
|
1890
1999
|
] })
|
|
1891
2000
|
] });
|
|
1892
2001
|
}
|
|
1893
2002
|
|
|
1894
2003
|
// src/cli/render/ink/panels/ShieldsPanel.tsx
|
|
1895
|
-
import { Box as
|
|
2004
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1896
2005
|
|
|
1897
2006
|
// src/protection.ts
|
|
1898
2007
|
var PROTECTIVE_SHIELD_DISCOUNTS = {
|
|
@@ -1900,7 +2009,7 @@ var PROTECTIVE_SHIELD_DISCOUNTS = {
|
|
|
1900
2009
|
};
|
|
1901
2010
|
|
|
1902
2011
|
// src/cli/render/ink/panels/ShieldsPanel.tsx
|
|
1903
|
-
import { jsx as
|
|
2012
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1904
2013
|
function ShieldsPanel({ summary, width, enabledShields = [] }) {
|
|
1905
2014
|
const enabled = new Set(enabledShields);
|
|
1906
2015
|
const impacts = rollupByShield(summary.sections);
|
|
@@ -1921,26 +2030,26 @@ function ShieldsPanel({ summary, width, enabledShields = [] }) {
|
|
|
1921
2030
|
const allCovered = !topRec && hitShields.some(
|
|
1922
2031
|
(r) => (PROTECTIVE_SHIELD_DISCOUNTS[r.shieldName] ?? 0) > 0 && enabled.has(r.shieldName)
|
|
1923
2032
|
);
|
|
1924
|
-
return /* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
2033
|
+
return /* @__PURE__ */ jsxs11(Box11, { borderStyle: "round", borderColor: "cyan", paddingX: 1, flexDirection: "column", width, children: [
|
|
2034
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: "cyan", children: "SHIELDS" }),
|
|
1926
2035
|
hitShields.map((impact) => {
|
|
1927
2036
|
const discount = PROTECTIVE_SHIELD_DISCOUNTS[impact.shieldName] ?? 0;
|
|
1928
2037
|
const isOn = enabled.has(impact.shieldName);
|
|
1929
2038
|
const noun = `op${impact.totalCatches !== 1 ? "s" : ""}`;
|
|
1930
|
-
return /* @__PURE__ */
|
|
1931
|
-
/* @__PURE__ */
|
|
1932
|
-
/* @__PURE__ */
|
|
1933
|
-
isOn ? /* @__PURE__ */
|
|
2039
|
+
return /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
2040
|
+
/* @__PURE__ */ jsx11(Box11, { width: 16, children: /* @__PURE__ */ jsx11(Text11, { bold: true, color: discount > 0 ? "cyan" : void 0, dimColor: discount === 0, children: impact.shieldName }) }),
|
|
2041
|
+
/* @__PURE__ */ jsx11(Box11, { width: 20, children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: `catches ${impact.totalCatches} ${noun}` }) }),
|
|
2042
|
+
isOn ? /* @__PURE__ */ jsx11(Text11, { bold: true, color: "green", children: `\u2713 enabled` }) : discount > 0 ? /* @__PURE__ */ jsx11(Text11, { bold: true, color: "green", children: `\u2192 blocks these reads in-path` }) : null
|
|
1934
2043
|
] }, impact.shieldName);
|
|
1935
2044
|
}),
|
|
1936
|
-
zeroHitEnabled.length > 0 ? /* @__PURE__ */
|
|
1937
|
-
zeroHitBuiltins.length > 0 ? /* @__PURE__ */
|
|
1938
|
-
topRec ? /* @__PURE__ */
|
|
2045
|
+
zeroHitEnabled.length > 0 ? /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, wrap: "truncate-end", children: "\u2713 active: " + zeroHitEnabled.join(" \xB7 ") + " (no hits in this history)" }) }) : null,
|
|
2046
|
+
zeroHitBuiltins.length > 0 ? /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, wrap: "truncate-end", children: zeroHitBuiltins.join(" \xB7 ") + " (no hits \u2014 install proactively)" }) }) : null,
|
|
2047
|
+
topRec ? /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsx11(Text11, { color: "cyan", bold: true, children: `\u2192 node9 shield enable ${topRec.shieldName} (start here \u2014 widest coverage)` }) }) : allCovered ? /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsx11(Text11, { color: "green", bold: true, children: "\u2713 recommended shields are enabled" }) }) : null
|
|
1939
2048
|
] });
|
|
1940
2049
|
}
|
|
1941
2050
|
|
|
1942
2051
|
// src/cli/render/ink/StaticScorecard.tsx
|
|
1943
|
-
import { Fragment as Fragment2, jsx as
|
|
2052
|
+
import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1944
2053
|
var MAX_WIDTH = 90;
|
|
1945
2054
|
function renderWidth() {
|
|
1946
2055
|
const term = process.stdout.columns ?? MAX_WIDTH;
|
|
@@ -1951,9 +2060,13 @@ function StaticScorecard({ input, rangeLabel, now }) {
|
|
|
1951
2060
|
const width = renderWidth();
|
|
1952
2061
|
const halfWidth = Math.floor((width - 1) / 2);
|
|
1953
2062
|
const leakCount = summary.leaks.length;
|
|
1954
|
-
const
|
|
2063
|
+
const decoyCount = summary.canaries.length;
|
|
2064
|
+
const hasCritical = decoyCount > 0 || leakCount > 0 || blockedCount > 0;
|
|
1955
2065
|
const criticalLabel = (() => {
|
|
1956
2066
|
const parts = [];
|
|
2067
|
+
if (decoyCount > 0) {
|
|
2068
|
+
parts.push(`${decoyCount} decoy${decoyCount !== 1 ? "s" : ""} tripped`);
|
|
2069
|
+
}
|
|
1957
2070
|
if (leakCount > 0) {
|
|
1958
2071
|
parts.push(`${leakCount} secret${leakCount !== 1 ? "s" : ""} leaked`);
|
|
1959
2072
|
}
|
|
@@ -1962,18 +2075,19 @@ function StaticScorecard({ input, rangeLabel, now }) {
|
|
|
1962
2075
|
}
|
|
1963
2076
|
return `Critical (${parts.join(" + ")})`;
|
|
1964
2077
|
})();
|
|
1965
|
-
return /* @__PURE__ */
|
|
1966
|
-
/* @__PURE__ */
|
|
1967
|
-
/* @__PURE__ */
|
|
1968
|
-
/* @__PURE__ */
|
|
1969
|
-
/* @__PURE__ */
|
|
1970
|
-
/* @__PURE__ */
|
|
2078
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingTop: 1, width, children: [
|
|
2079
|
+
/* @__PURE__ */ jsx12(Header, { rangeLabel }),
|
|
2080
|
+
/* @__PURE__ */ jsx12(SeverityBand, { label: "Spend & activity", width }),
|
|
2081
|
+
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
|
|
2082
|
+
/* @__PURE__ */ jsx12(CostPanel, { summary, width: halfWidth, keyed: input.keyed }),
|
|
2083
|
+
/* @__PURE__ */ jsx12(ActivityPanel, { summary, scan: input.scan, width: halfWidth })
|
|
1971
2084
|
] }),
|
|
1972
|
-
hasCritical ? /* @__PURE__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
1974
|
-
/* @__PURE__ */
|
|
1975
|
-
|
|
1976
|
-
/* @__PURE__ */
|
|
2085
|
+
hasCritical ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2086
|
+
/* @__PURE__ */ jsx12(SeverityBand, { label: criticalLabel, width }),
|
|
2087
|
+
decoyCount > 0 ? /* @__PURE__ */ jsx12(Box12, { flexDirection: "row", gap: 1, children: /* @__PURE__ */ jsx12(DecoyPanel, { summary, width: halfWidth, now }) }) : null,
|
|
2088
|
+
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
|
|
2089
|
+
/* @__PURE__ */ jsx12(LeaksPanel, { summary, width: halfWidth, now }),
|
|
2090
|
+
/* @__PURE__ */ jsx12(
|
|
1977
2091
|
BlockedPanel,
|
|
1978
2092
|
{
|
|
1979
2093
|
summary,
|
|
@@ -1983,8 +2097,8 @@ function StaticScorecard({ input, rangeLabel, now }) {
|
|
|
1983
2097
|
)
|
|
1984
2098
|
] })
|
|
1985
2099
|
] }) : null,
|
|
1986
|
-
input.blastExposures > 0 ? /* @__PURE__ */
|
|
1987
|
-
/* @__PURE__ */
|
|
2100
|
+
input.blastExposures > 0 ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2101
|
+
/* @__PURE__ */ jsx12(
|
|
1988
2102
|
SeverityBand,
|
|
1989
2103
|
{
|
|
1990
2104
|
label: (() => {
|
|
@@ -1997,7 +2111,7 @@ function StaticScorecard({ input, rangeLabel, now }) {
|
|
|
1997
2111
|
width
|
|
1998
2112
|
}
|
|
1999
2113
|
),
|
|
2000
|
-
/* @__PURE__ */
|
|
2114
|
+
/* @__PURE__ */ jsx12(
|
|
2001
2115
|
BlastRadiusPanel,
|
|
2002
2116
|
{
|
|
2003
2117
|
blast: input.blast,
|
|
@@ -2020,10 +2134,10 @@ function StaticScorecard({ input, rangeLabel, now }) {
|
|
|
2020
2134
|
parts.push(
|
|
2021
2135
|
`${loopCount} loop${loopCount !== 1 ? "s" : ""}${wastedCalls > 0 ? ` \xB7 ${wastedCalls} repeat calls (${wastePct}% of all calls)` : ""}`
|
|
2022
2136
|
);
|
|
2023
|
-
return /* @__PURE__ */
|
|
2024
|
-
/* @__PURE__ */
|
|
2025
|
-
/* @__PURE__ */
|
|
2026
|
-
/* @__PURE__ */
|
|
2137
|
+
return /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2138
|
+
/* @__PURE__ */ jsx12(SeverityBand, { label: `Medium (${parts.join(" \xB7 ")})`, width }),
|
|
2139
|
+
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
|
|
2140
|
+
/* @__PURE__ */ jsx12(
|
|
2027
2141
|
ReviewQueuePanel,
|
|
2028
2142
|
{
|
|
2029
2143
|
summary: input.summary,
|
|
@@ -2031,16 +2145,16 @@ function StaticScorecard({ input, rangeLabel, now }) {
|
|
|
2031
2145
|
enabledShields: input.enabledShields
|
|
2032
2146
|
}
|
|
2033
2147
|
),
|
|
2034
|
-
/* @__PURE__ */
|
|
2148
|
+
/* @__PURE__ */ jsx12(AgentLoopsPanel, { loopFindings: input.scan.loopFindings, width: halfWidth })
|
|
2035
2149
|
] })
|
|
2036
2150
|
] });
|
|
2037
2151
|
})(),
|
|
2038
|
-
/* @__PURE__ */
|
|
2039
|
-
/* @__PURE__ */
|
|
2152
|
+
/* @__PURE__ */ jsx12(SeverityBand, { label: "Recommended action", width }),
|
|
2153
|
+
/* @__PURE__ */ jsx12(ShieldsPanel, { summary: input.summary, width, enabledShields: input.enabledShields })
|
|
2040
2154
|
] });
|
|
2041
2155
|
}
|
|
2042
2156
|
function renderScanScorecardInk(input, rangeLabel) {
|
|
2043
|
-
const { unmount } = render(/* @__PURE__ */
|
|
2157
|
+
const { unmount } = render(/* @__PURE__ */ jsx12(StaticScorecard, { input, rangeLabel }), {
|
|
2044
2158
|
patchConsole: false
|
|
2045
2159
|
});
|
|
2046
2160
|
unmount();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|