@seekrit/cli 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +246 -189
- package/dist/{mcp-Dgj-7S9y.js → mcp-Clul2n4l.js} +1 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -25,11 +25,15 @@ const policyMethodSchema = z.string().trim().toUpperCase().regex(/^[A-Z]{3,10}$/
|
|
|
25
25
|
*/
|
|
26
26
|
const policyPathSchema = z.string().trim().min(1).max(512).startsWith("/", "path pattern must start with /").refine((p) => !p.includes("?"), { message: "path patterns match the path only, not the query" });
|
|
27
27
|
const policySecretNameSchema = z.string().trim().regex(/^[A-Za-z0-9_]+$/, "secret names are letters, digits, and underscores");
|
|
28
|
+
/** A TCP port. 0 is refused: it never matches a connection, so a rule naming it is a mistake. */
|
|
29
|
+
const policyPortSchema = z.number().int().min(1).max(65535);
|
|
28
30
|
const agentPolicyRuleSchema = z.object({
|
|
29
31
|
host: policyHostSchema,
|
|
30
32
|
methods: z.array(policyMethodSchema).max(16).default([]),
|
|
31
33
|
paths: z.array(policyPathSchema).max(64).default([]),
|
|
32
34
|
allow: z.array(policySecretNameSchema).max(64).default([]),
|
|
35
|
+
https_ports: z.array(policyPortSchema).max(32).optional(),
|
|
36
|
+
http_ports: z.array(policyPortSchema).max(32).optional(),
|
|
33
37
|
label: z.string().trim().max(120).optional()
|
|
34
38
|
});
|
|
35
39
|
z.object({
|
|
@@ -48,11 +52,65 @@ z.object({
|
|
|
48
52
|
});
|
|
49
53
|
z.object({
|
|
50
54
|
host: policyHostSchema,
|
|
55
|
+
/** How the agent reaches the host. Omit for a destination the operator fixed. */
|
|
56
|
+
transport: z.object({
|
|
57
|
+
scheme: z.enum(["https", "http"]),
|
|
58
|
+
port: policyPortSchema
|
|
59
|
+
}).optional(),
|
|
51
60
|
method: policyMethodSchema,
|
|
52
61
|
path: z.string().trim().min(1).max(2048),
|
|
53
62
|
secret: policySecretNameSchema.optional()
|
|
54
63
|
});
|
|
55
64
|
/**
|
|
65
|
+
* The ports a rule covers, with the default applied.
|
|
66
|
+
*
|
|
67
|
+
* **Absent does not mean any here**, unlike methods and paths. A bare hostname
|
|
68
|
+
* reads as `https://host`, so a rule naming no ports covers HTTPS on 443 and
|
|
69
|
+
* nothing else: another port on the same name is another service (often an
|
|
70
|
+
* admin one), and cleartext puts the substituted credential on the wire. The
|
|
71
|
+
* default applies only when *neither* list says anything — `http_ports: [8080]`
|
|
72
|
+
* alone means exactly that, not that plus 443.
|
|
73
|
+
*/
|
|
74
|
+
function policyRulePorts(rule) {
|
|
75
|
+
const norm = (ports) => [...new Set(ports ?? [])].sort((a, b) => a - b);
|
|
76
|
+
const https = norm(rule.https_ports);
|
|
77
|
+
const http = norm(rule.http_ports);
|
|
78
|
+
if (https.length === 0 && http.length === 0) return {
|
|
79
|
+
https: [443],
|
|
80
|
+
http: []
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
https,
|
|
84
|
+
http
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** True when a rule covers HTTPS on 443 and nothing else — all v1 can say. */
|
|
88
|
+
function isDefaultPolicyPorts(rule) {
|
|
89
|
+
const { https, http } = policyRulePorts(rule);
|
|
90
|
+
return http.length === 0 && https.length === 1 && https[0] === 443;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `https:443,8443 http:5000`, or `null` for the default — for the editor's
|
|
94
|
+
* collapsed rows, the publish diff, and the CLI, which only mention ports when
|
|
95
|
+
* a rule says something about them.
|
|
96
|
+
*/
|
|
97
|
+
function describePolicyPorts(rule) {
|
|
98
|
+
if (isDefaultPolicyPorts(rule)) return null;
|
|
99
|
+
const { https, http } = policyRulePorts(rule);
|
|
100
|
+
const parts = [];
|
|
101
|
+
if (https.length > 0) parts.push(`https:${https.join(",")}`);
|
|
102
|
+
if (http.length > 0) parts.push(`http:${http.join(",")}`);
|
|
103
|
+
return parts.join(" ");
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The bundle version to publish these rules as: v1 unless a rule needs ports v1
|
|
107
|
+
* cannot express. Keeping the old version wherever it suffices is what lets a
|
|
108
|
+
* fleet upgrade its proxies after the dashboard rather than in lockstep.
|
|
109
|
+
*/
|
|
110
|
+
function policyBundleVersionFor(rules) {
|
|
111
|
+
return rules.every(isDefaultPolicyPorts) ? 1 : 2;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
56
114
|
* Serialize a bundle to the exact bytes that get signed and transported.
|
|
57
115
|
*
|
|
58
116
|
* Explicit field order rather than a generic sorted-key walk: the field list is
|
|
@@ -61,6 +119,10 @@ z.object({
|
|
|
61
119
|
* arrays are kept, since their emptiness is meaningful (any/any/none).
|
|
62
120
|
*/
|
|
63
121
|
function canonicalizeAgentPolicy(bundle) {
|
|
122
|
+
if (bundle.v < 2) {
|
|
123
|
+
const index = bundle.rules.findIndex((rule) => !isDefaultPolicyPorts(rule));
|
|
124
|
+
if (index !== -1) throw new AgentPolicyError(`rule ${index + 1} names ports, which need a v2 bundle (see policyBundleVersionFor)`);
|
|
125
|
+
}
|
|
64
126
|
const rules = bundle.rules.map((rule) => {
|
|
65
127
|
const out = {
|
|
66
128
|
host: rule.host.trim().toLowerCase(),
|
|
@@ -68,6 +130,11 @@ function canonicalizeAgentPolicy(bundle) {
|
|
|
68
130
|
paths: rule.paths.map((p) => p.trim()),
|
|
69
131
|
allow: [...rule.allow].sort()
|
|
70
132
|
};
|
|
133
|
+
if (bundle.v >= 2) {
|
|
134
|
+
const ports = policyRulePorts(rule);
|
|
135
|
+
out.https_ports = ports.https;
|
|
136
|
+
out.http_ports = ports.http;
|
|
137
|
+
}
|
|
71
138
|
if (rule.label?.trim()) out.label = rule.label.trim();
|
|
72
139
|
return out;
|
|
73
140
|
});
|
|
@@ -158,8 +225,13 @@ function parseAgentPolicyUnverified(envelope) {
|
|
|
158
225
|
} catch (e) {
|
|
159
226
|
throw new AgentPolicyError(`policy bundle body is unreadable: ${e.message}`);
|
|
160
227
|
}
|
|
161
|
-
if (body?.v !== 1) throw new AgentPolicyError(`unsupported policy bundle version ${String(body?.v)}`);
|
|
228
|
+
if (typeof body?.v !== "number" || body.v < 1 || body.v > 2) throw new AgentPolicyError(`unsupported policy bundle version ${String(body?.v)}`);
|
|
162
229
|
if (!Array.isArray(body.rules) || !body.signer?.jwk) throw new AgentPolicyError("policy bundle is missing rules or signer");
|
|
230
|
+
body.rules.forEach((rule, i) => {
|
|
231
|
+
const carriesPorts = rule.https_ports !== void 0 || rule.http_ports !== void 0;
|
|
232
|
+
if (body.v < 2 && carriesPorts) throw new AgentPolicyError(`policy bundle v${body.v} rule ${i + 1} carries https_ports/http_ports, which need a v2 bundle`);
|
|
233
|
+
for (const port of [...rule.https_ports ?? [], ...rule.http_ports ?? []]) if (!policyPortSchema.safeParse(port).success) throw new AgentPolicyError(`policy bundle rule ${i + 1} names an invalid port ${port}`);
|
|
234
|
+
});
|
|
163
235
|
return body;
|
|
164
236
|
}
|
|
165
237
|
/**
|
|
@@ -209,6 +281,11 @@ function ruleCoversPath(rule, path) {
|
|
|
209
281
|
if (rule.paths.length === 0) return true;
|
|
210
282
|
return rule.paths.some((p) => matchPolicyPath(p, path));
|
|
211
283
|
}
|
|
284
|
+
function ruleReaches(rule, transport) {
|
|
285
|
+
if (!transport) return true;
|
|
286
|
+
const ports = policyRulePorts(rule);
|
|
287
|
+
return (transport.scheme === "https" ? ports.https : ports.http).includes(transport.port);
|
|
288
|
+
}
|
|
212
289
|
/**
|
|
213
290
|
* Decide a request against an ordered rule set, first match wins — the same
|
|
214
291
|
* evaluation `RuleSet::decide` performs in the proxy, including which rule
|
|
@@ -218,11 +295,14 @@ function ruleCoversPath(rule, path) {
|
|
|
218
295
|
function evaluatePolicy(rules, query) {
|
|
219
296
|
const host = query.host.trim().toLowerCase();
|
|
220
297
|
let hostMatched = false;
|
|
298
|
+
let portMatched = false;
|
|
221
299
|
let pathMatchedIndex = null;
|
|
222
300
|
for (let i = 0; i < rules.length; i++) {
|
|
223
301
|
const rule = rules[i];
|
|
224
302
|
if (rule.host.trim().toLowerCase() !== host) continue;
|
|
225
303
|
hostMatched = true;
|
|
304
|
+
if (!ruleReaches(rule, query.transport)) continue;
|
|
305
|
+
portMatched = true;
|
|
226
306
|
const pathsOk = ruleCoversPath(rule, query.path);
|
|
227
307
|
if (pathsOk && ruleCoversMethod(rule, query.method)) {
|
|
228
308
|
if (query.secret !== void 0 && !rule.allow.includes(query.secret)) return {
|
|
@@ -240,8 +320,12 @@ function evaluatePolicy(rules, query) {
|
|
|
240
320
|
decision: "method_not_allowed",
|
|
241
321
|
ruleIndex: pathMatchedIndex
|
|
242
322
|
};
|
|
323
|
+
if (!hostMatched) return {
|
|
324
|
+
decision: "no_rule",
|
|
325
|
+
ruleIndex: null
|
|
326
|
+
};
|
|
243
327
|
return {
|
|
244
|
-
decision:
|
|
328
|
+
decision: portMatched ? "path_not_allowed" : "port_not_allowed",
|
|
245
329
|
ruleIndex: null
|
|
246
330
|
};
|
|
247
331
|
}
|
|
@@ -251,6 +335,7 @@ function describePolicyVerdict(verdict, rules) {
|
|
|
251
335
|
switch (verdict.decision) {
|
|
252
336
|
case "allow": return `permitted by rule ${(verdict.ruleIndex ?? 0) + 1}${rule?.label ? ` (${rule.label})` : ""}`;
|
|
253
337
|
case "no_rule": return "no rule covers this host";
|
|
338
|
+
case "port_not_allowed": return "this host has rules, but none cover this scheme and port";
|
|
254
339
|
case "method_not_allowed": return `rule ${(verdict.ruleIndex ?? 0) + 1} covers this path but not this method`;
|
|
255
340
|
case "path_not_allowed": return "this host has rules, but none cover this path";
|
|
256
341
|
case "secret_not_allowed": return `rule ${(verdict.ruleIndex ?? 0) + 1} permits the request but not this secret`;
|
|
@@ -291,6 +376,7 @@ function normalizeRule(rule) {
|
|
|
291
376
|
methods: rule.methods.map((m) => m.toUpperCase()).sort(),
|
|
292
377
|
paths: [...rule.paths],
|
|
293
378
|
allow: [...rule.allow].sort(),
|
|
379
|
+
ports: policyRulePorts(rule),
|
|
294
380
|
label: rule.label?.trim() || void 0
|
|
295
381
|
};
|
|
296
382
|
}
|
|
@@ -319,6 +405,7 @@ const activityEntrySchema = z.object({
|
|
|
319
405
|
decision: z.enum([
|
|
320
406
|
"allow",
|
|
321
407
|
"no_rule",
|
|
408
|
+
"port_not_allowed",
|
|
322
409
|
"method_not_allowed",
|
|
323
410
|
"path_not_allowed",
|
|
324
411
|
"secret_not_allowed",
|
|
@@ -354,8 +441,7 @@ z.object({
|
|
|
354
441
|
*/
|
|
355
442
|
entries: z.array(activityEntrySchema).min(1).max(500)
|
|
356
443
|
});
|
|
357
|
-
|
|
358
|
-
//#region ../../packages/core/src/agent-review.ts
|
|
444
|
+
// ../../packages/core/src/agent-review.ts
|
|
359
445
|
function emptyStats() {
|
|
360
446
|
return {
|
|
361
447
|
allows: 0,
|
|
@@ -565,8 +651,7 @@ z.object({
|
|
|
565
651
|
z.object({
|
|
566
652
|
/** The presented token. In the body, never a URL — it is a credential. */
|
|
567
653
|
token: z.string().trim().min(8).max(512) });
|
|
568
|
-
|
|
569
|
-
//#region ../../packages/core/src/archive.ts
|
|
654
|
+
// ../../packages/core/src/archive.ts
|
|
570
655
|
/**
|
|
571
656
|
* The **break-glass archive** format: one signed JSON file holding everything
|
|
572
657
|
* seekrit stores for an org, in the form seekrit stores it — ciphertext stays
|
|
@@ -951,8 +1036,7 @@ const ENTITLEMENT_KEYS = Object.keys({
|
|
|
951
1036
|
metric: "monthly_resolves"
|
|
952
1037
|
}
|
|
953
1038
|
});
|
|
954
|
-
|
|
955
|
-
//#region ../../packages/core/src/plans.ts
|
|
1039
|
+
// ../../packages/core/src/plans.ts
|
|
956
1040
|
const PLAN_FAMILIES = {
|
|
957
1041
|
free: {
|
|
958
1042
|
id: "free",
|
|
@@ -986,8 +1070,7 @@ const PLAN_FAMILIES = {
|
|
|
986
1070
|
const PLAN_FAMILY_IDS = Object.keys(PLAN_FAMILIES);
|
|
987
1071
|
/** Plan families a plan picker should render. Excludes families marked `hidden`. */
|
|
988
1072
|
const VISIBLE_PLAN_FAMILY_IDS = PLAN_FAMILY_IDS.filter((family) => !PLAN_FAMILIES[family].hidden);
|
|
989
|
-
|
|
990
|
-
//#region ../../packages/core/src/billing.ts
|
|
1073
|
+
// ../../packages/core/src/billing.ts
|
|
991
1074
|
/**
|
|
992
1075
|
* Lifecycle states a subscription can be in. Mirrors the biller's own states
|
|
993
1076
|
* (Stripe) but is provider-neutral so a different biller could map onto it.
|
|
@@ -999,8 +1082,7 @@ const SUBSCRIPTION_STATUSES = [
|
|
|
999
1082
|
"canceled",
|
|
1000
1083
|
"paused"
|
|
1001
1084
|
];
|
|
1002
|
-
|
|
1003
|
-
//#region ../../packages/core/src/branches.ts
|
|
1085
|
+
// ../../packages/core/src/branches.ts
|
|
1004
1086
|
/**
|
|
1005
1087
|
* Branch (ephemeral) environments — a per-PR/preview overlay on an existing
|
|
1006
1088
|
* application environment.
|
|
@@ -1044,8 +1126,7 @@ function parseBranchTtl(input) {
|
|
|
1044
1126
|
if (multiplier === void 0) return null;
|
|
1045
1127
|
return value * multiplier;
|
|
1046
1128
|
}
|
|
1047
|
-
|
|
1048
|
-
//#region ../../packages/core/src/change-requests.ts
|
|
1129
|
+
// ../../packages/core/src/change-requests.ts
|
|
1049
1130
|
const CHANGE_REQUEST_STATUSES = [
|
|
1050
1131
|
"open",
|
|
1051
1132
|
"applied",
|
|
@@ -1061,8 +1142,7 @@ function describeChange(kind, name, restoreFrom) {
|
|
|
1061
1142
|
case "restore": return `restore ${name} to v${restoreFrom ?? "?"}`;
|
|
1062
1143
|
}
|
|
1063
1144
|
}
|
|
1064
|
-
|
|
1065
|
-
//#region ../../packages/core/src/connect.ts
|
|
1145
|
+
// ../../packages/core/src/connect.ts
|
|
1066
1146
|
/** Workload authentication is separate from secret-decryption principals. */
|
|
1067
1147
|
const workloadPublicKeySchema = z.object({
|
|
1068
1148
|
kty: z.literal("EC"),
|
|
@@ -1196,8 +1276,7 @@ z.object({
|
|
|
1196
1276
|
body: z.string().max(65536).optional()
|
|
1197
1277
|
}).strict().refine((v) => !["GET", "HEAD"].includes(v.method) || v.body === void 0, "GET and HEAD cannot carry a body");
|
|
1198
1278
|
z.object({ audience: audienceSchema }).strict();
|
|
1199
|
-
|
|
1200
|
-
//#region ../../packages/core/src/dotenv.ts
|
|
1279
|
+
// ../../packages/core/src/dotenv.ts
|
|
1201
1280
|
/**
|
|
1202
1281
|
* `.env` parsing and serialization — the canonical definition of what a `.env`
|
|
1203
1282
|
* file means to seekrit. `apps/run/src/dotenv.rs` is a faithful port of the
|
|
@@ -1320,8 +1399,7 @@ function skipSpace(content, from, end) {
|
|
|
1320
1399
|
while (i < end && (content[i] === " " || content[i] === " ")) i++;
|
|
1321
1400
|
return i;
|
|
1322
1401
|
}
|
|
1323
|
-
|
|
1324
|
-
//#region ../../packages/core/src/ids.ts
|
|
1402
|
+
// ../../packages/core/src/ids.ts
|
|
1325
1403
|
const ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
1326
1404
|
/**
|
|
1327
1405
|
* Generate a prefixed, URL-safe random ID, e.g. `org_h9K2x…`.
|
|
@@ -1338,8 +1416,7 @@ function randomId(prefix, length = 24) {
|
|
|
1338
1416
|
}
|
|
1339
1417
|
return `${prefix}_${out}`;
|
|
1340
1418
|
}
|
|
1341
|
-
|
|
1342
|
-
//#region ../../packages/core/src/interpolate.ts
|
|
1419
|
+
// ../../packages/core/src/interpolate.ts
|
|
1343
1420
|
/**
|
|
1344
1421
|
* Secret references: `${OTHER_SECRET}` inside a secret value.
|
|
1345
1422
|
*
|
|
@@ -1484,8 +1561,7 @@ function interpolateSecrets(values) {
|
|
|
1484
1561
|
unresolved: [...unresolved].sort()
|
|
1485
1562
|
};
|
|
1486
1563
|
}
|
|
1487
|
-
|
|
1488
|
-
//#region ../../packages/core/src/json-value.ts
|
|
1564
|
+
// ../../packages/core/src/json-value.ts
|
|
1489
1565
|
/**
|
|
1490
1566
|
* Whether a value is worth *offering* a JSON view for: an object or array.
|
|
1491
1567
|
*
|
|
@@ -1939,8 +2015,7 @@ z.discriminatedUnion("provider", [
|
|
|
1939
2015
|
mintGcpLeaseSchema,
|
|
1940
2016
|
mintMongoLeaseSchema
|
|
1941
2017
|
]);
|
|
1942
|
-
|
|
1943
|
-
//#region ../../packages/core/src/providers/aws.ts
|
|
2018
|
+
// ../../packages/core/src/providers/aws.ts
|
|
1944
2019
|
/**
|
|
1945
2020
|
* The one-time IAM setup an admin performs so seekrit can assume the target
|
|
1946
2021
|
* role. Analogue of `sshHostSetupInstructions` / `postgresGroupBootstrapSql` —
|
|
@@ -1967,8 +2042,7 @@ function awsTrustPolicyInstructions(config) {
|
|
|
1967
2042
|
`# on ${config.roleArn}. seekrit only ever calls AssumeRole on this role.`
|
|
1968
2043
|
].join("\n");
|
|
1969
2044
|
}
|
|
1970
|
-
|
|
1971
|
-
//#region ../../packages/core/src/providers/gcp.ts
|
|
2045
|
+
// ../../packages/core/src/providers/gcp.ts
|
|
1972
2046
|
/**
|
|
1973
2047
|
* The one-time IAM setup an admin performs so seekrit can impersonate the target
|
|
1974
2048
|
* service account. Analogue of `awsTrustPolicyInstructions` — printed for the
|
|
@@ -1990,8 +2064,7 @@ function gcpSetupInstructions(config) {
|
|
|
1990
2064
|
"gcloud services enable iamcredentials.googleapis.com"
|
|
1991
2065
|
].join("\n");
|
|
1992
2066
|
}
|
|
1993
|
-
|
|
1994
|
-
//#region ../../packages/core/src/providers/mongodb.ts
|
|
2067
|
+
// ../../packages/core/src/providers/mongodb.ts
|
|
1995
2068
|
/**
|
|
1996
2069
|
* The one-time setup an admin performs so seekrit can provision users: create a
|
|
1997
2070
|
* dedicated provisioning user with `userAdmin` on the target database (or
|
|
@@ -2016,8 +2089,7 @@ function mongoAdminSetupInstructions(config) {
|
|
|
2016
2089
|
`// --uri "mongodb://seekrit_provisioner:<pwd>@${config.connection.host}:${config.connection.port}/?authSource=${authSource}"`
|
|
2017
2090
|
].join("\n");
|
|
2018
2091
|
}
|
|
2019
|
-
|
|
2020
|
-
//#region ../../packages/core/src/providers/postgres.ts
|
|
2092
|
+
// ../../packages/core/src/providers/postgres.ts
|
|
2021
2093
|
/**
|
|
2022
2094
|
* The one-time setup SQL an admin runs to create the shared group role that a
|
|
2023
2095
|
* read-only / read-write target's leased credentials inherit. Idempotent (safe
|
|
@@ -2048,8 +2120,7 @@ function postgresGroupBootstrapSql(config) {
|
|
|
2048
2120
|
if (config.accessLevel === "readwrite") lines.push(`GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "${schema}" TO ${group};`, `ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT USAGE, SELECT ON SEQUENCES TO ${group};`);
|
|
2049
2121
|
return lines.join("\n");
|
|
2050
2122
|
}
|
|
2051
|
-
|
|
2052
|
-
//#region ../../packages/core/src/providers/ssh.ts
|
|
2123
|
+
// ../../packages/core/src/providers/ssh.ts
|
|
2053
2124
|
/**
|
|
2054
2125
|
* The one-time host setup an admin runs so a target's certificates are accepted.
|
|
2055
2126
|
* Analogue of `postgresGroupBootstrapSql` — displayed for the admin to run, not
|
|
@@ -2069,8 +2140,7 @@ function sshHostSetupInstructions(config) {
|
|
|
2069
2140
|
"sudo systemctl reload sshd"
|
|
2070
2141
|
].join("\n");
|
|
2071
2142
|
}
|
|
2072
|
-
|
|
2073
|
-
//#region ../../packages/core/src/types.ts
|
|
2143
|
+
// ../../packages/core/src/types.ts
|
|
2074
2144
|
const AUDIT_ACTIONS = [
|
|
2075
2145
|
"workload.created",
|
|
2076
2146
|
"workload.updated",
|
|
@@ -2301,8 +2371,7 @@ const NOTIFICATION_TYPE_META = {
|
|
|
2301
2371
|
defaultEnabled: true
|
|
2302
2372
|
}
|
|
2303
2373
|
};
|
|
2304
|
-
|
|
2305
|
-
//#region ../../packages/core/src/schemas.ts
|
|
2374
|
+
// ../../packages/core/src/schemas.ts
|
|
2306
2375
|
/** URL-safe identifier segment: `my-app`, `production`, … */
|
|
2307
2376
|
const slugSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "must be lowercase alphanumeric with hyphens");
|
|
2308
2377
|
const nameSchema = z.string().trim().min(1).max(128);
|
|
@@ -2724,8 +2793,7 @@ z.object({
|
|
|
2724
2793
|
*/
|
|
2725
2794
|
status: settableRotationStatusSchema.optional()
|
|
2726
2795
|
}).refine((v) => v.intervalSeconds !== void 0 || v.config !== void 0 || v.status !== void 0, "provide at least one of intervalSeconds, config, or status");
|
|
2727
|
-
|
|
2728
|
-
//#region ../../packages/core/src/sync.ts
|
|
2796
|
+
// ../../packages/core/src/sync.ts
|
|
2729
2797
|
/**
|
|
2730
2798
|
* Third-party sync: seekrit as source of truth, pushing an environment's
|
|
2731
2799
|
* resolved secrets out to a platform that holds its own copy (Vercel project
|
|
@@ -4209,8 +4277,7 @@ z.object({
|
|
|
4209
4277
|
mode: z.enum(["auto", "manual"]).optional(),
|
|
4210
4278
|
enabled: z.boolean().optional()
|
|
4211
4279
|
});
|
|
4212
|
-
|
|
4213
|
-
//#region ../../packages/core/src/vault.ts
|
|
4280
|
+
// ../../packages/core/src/vault.ts
|
|
4214
4281
|
/** One encrypted credential within a connection. */
|
|
4215
4282
|
const VAULT_SLOTS = [
|
|
4216
4283
|
"oauth2",
|
|
@@ -4494,8 +4561,7 @@ z.object({
|
|
|
4494
4561
|
userRef: vaultUserRefSchema,
|
|
4495
4562
|
leaseId: leaseIdSchema
|
|
4496
4563
|
});
|
|
4497
|
-
|
|
4498
|
-
//#region ../../packages/crypto/src/encoding.ts
|
|
4564
|
+
// ../../packages/crypto/src/encoding.ts
|
|
4499
4565
|
const CHUNK = 32768;
|
|
4500
4566
|
/** Base64url (no padding) — portable across browsers, Workers, and Node. */
|
|
4501
4567
|
function toBase64Url(bytes) {
|
|
@@ -4526,8 +4592,7 @@ function utf8Encode(text) {
|
|
|
4526
4592
|
function utf8Decode(bytes) {
|
|
4527
4593
|
return new TextDecoder().decode(bytes);
|
|
4528
4594
|
}
|
|
4529
|
-
|
|
4530
|
-
//#region ../../packages/crypto/src/errors.ts
|
|
4595
|
+
// ../../packages/crypto/src/errors.ts
|
|
4531
4596
|
var SeekritCryptoError = class extends Error {
|
|
4532
4597
|
code;
|
|
4533
4598
|
constructor(code, message) {
|
|
@@ -4547,8 +4612,7 @@ function splitBlob(blob, prefix, segments) {
|
|
|
4547
4612
|
if (parts.length !== segments + 1 || parts.some((p) => p.length === 0)) throw new SeekritCryptoError("MALFORMED_BLOB", `malformed "${prefix}" blob`);
|
|
4548
4613
|
return parts.slice(1);
|
|
4549
4614
|
}
|
|
4550
|
-
|
|
4551
|
-
//#region ../../packages/crypto/src/aes.ts
|
|
4615
|
+
// ../../packages/crypto/src/aes.ts
|
|
4552
4616
|
const SECRET_PREFIX = "sc1";
|
|
4553
4617
|
const IV_LENGTH$1 = 12;
|
|
4554
4618
|
/** Generate a fresh 256-bit data encryption key for an environment. */
|
|
@@ -4593,8 +4657,7 @@ async function decryptSecret(dek, blob, aad) {
|
|
|
4593
4657
|
function secretAad(environmentId, secretName) {
|
|
4594
4658
|
return `${environmentId}/${secretName}`;
|
|
4595
4659
|
}
|
|
4596
|
-
|
|
4597
|
-
//#region ../../packages/crypto/src/keys.ts
|
|
4660
|
+
// ../../packages/crypto/src/keys.ts
|
|
4598
4661
|
async function generateKeyPair() {
|
|
4599
4662
|
const pair = await crypto.subtle.generateKey({
|
|
4600
4663
|
name: "ECDH",
|
|
@@ -4627,8 +4690,7 @@ async function importPrivateKeyPkcs8(pkcs8) {
|
|
|
4627
4690
|
namedCurve: "P-256"
|
|
4628
4691
|
}, true, ["deriveBits"]);
|
|
4629
4692
|
}
|
|
4630
|
-
|
|
4631
|
-
//#region ../../packages/crypto/src/wrap.ts
|
|
4693
|
+
// ../../packages/crypto/src/wrap.ts
|
|
4632
4694
|
/**
|
|
4633
4695
|
* ECIES-style key wrapping: an ephemeral P-256 keypair performs ECDH against
|
|
4634
4696
|
* the recipient's public key; the shared secret is run through HKDF-SHA256 to
|
|
@@ -4696,8 +4758,7 @@ async function unwrapDek(wrapped, privateKey) {
|
|
|
4696
4758
|
throw new SeekritCryptoError("DECRYPT_FAILED", "DEK unwrap failed: wrong private key or tampered grant");
|
|
4697
4759
|
}
|
|
4698
4760
|
}
|
|
4699
|
-
|
|
4700
|
-
//#region ../../packages/crypto/src/aws.ts
|
|
4761
|
+
// ../../packages/crypto/src/aws.ts
|
|
4701
4762
|
/** Generate the ephemeral P-256 keypair a client uses to receive one AWS lease. */
|
|
4702
4763
|
async function generateAwsRecipientKeyPair() {
|
|
4703
4764
|
return generateKeyPair();
|
|
@@ -4724,8 +4785,7 @@ async function unwrapAwsCredential(wrapped, privateKeyJwk) {
|
|
|
4724
4785
|
region: c.region
|
|
4725
4786
|
};
|
|
4726
4787
|
}
|
|
4727
|
-
|
|
4728
|
-
//#region ../../packages/crypto/src/gcp.ts
|
|
4788
|
+
// ../../packages/crypto/src/gcp.ts
|
|
4729
4789
|
/** Generate the ephemeral P-256 keypair a client uses to receive one GCP lease. */
|
|
4730
4790
|
async function generateGcpRecipientKeyPair() {
|
|
4731
4791
|
return generateKeyPair();
|
|
@@ -4751,8 +4811,7 @@ async function unwrapGcpCredential(wrapped, privateKeyJwk) {
|
|
|
4751
4811
|
scopes: c.scopes
|
|
4752
4812
|
};
|
|
4753
4813
|
}
|
|
4754
|
-
|
|
4755
|
-
//#region ../../packages/crypto/src/kms.ts
|
|
4814
|
+
// ../../packages/crypto/src/kms.ts
|
|
4756
4815
|
/**
|
|
4757
4816
|
* Client-side KMS envelope encryption. A managed `encrypt` key is a 256-bit
|
|
4758
4817
|
* AES-GCM key whose material reaches the client only as a `wd1.` grant (wrapped
|
|
@@ -4882,8 +4941,7 @@ async function decryptDataKey(material, wrapped) {
|
|
|
4882
4941
|
throw new SeekritCryptoError("DECRYPT_FAILED", "data key unwrap failed: wrong key or tampered blob");
|
|
4883
4942
|
}
|
|
4884
4943
|
}
|
|
4885
|
-
|
|
4886
|
-
//#region ../../packages/crypto/src/random.ts
|
|
4944
|
+
// ../../packages/crypto/src/random.ts
|
|
4887
4945
|
const ALPHABETS = {
|
|
4888
4946
|
alphanumeric: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
4889
4947
|
hex: "0123456789abcdef",
|
|
@@ -4919,8 +4977,7 @@ function generateSecretValue(length, alphabet = "alphanumeric") {
|
|
|
4919
4977
|
}
|
|
4920
4978
|
return out;
|
|
4921
4979
|
}
|
|
4922
|
-
|
|
4923
|
-
//#region ../../packages/crypto/src/mongodb.ts
|
|
4980
|
+
// ../../packages/crypto/src/mongodb.ts
|
|
4924
4981
|
/** Generate the ephemeral P-256 keypair a client uses to receive one MongoDB lease. */
|
|
4925
4982
|
async function generateMongoRecipientKeyPair() {
|
|
4926
4983
|
return generateKeyPair();
|
|
@@ -4954,8 +5011,7 @@ async function unwrapMongoCredential(wrapped, privateKeyJwk) {
|
|
|
4954
5011
|
function mongoConnectionUri(cred) {
|
|
4955
5012
|
return `mongodb://${encodeURIComponent(cred.username)}:${encodeURIComponent(cred.password)}@${cred.host}:${cred.port}/${cred.database}?authSource=${cred.authSource}`;
|
|
4956
5013
|
}
|
|
4957
|
-
|
|
4958
|
-
//#region ../../packages/crypto/src/mysql.ts
|
|
5014
|
+
// ../../packages/crypto/src/mysql.ts
|
|
4959
5015
|
/**
|
|
4960
5016
|
* Client-side construction of a MySQL/MariaDB `mysql_native_password`
|
|
4961
5017
|
* authentication string, for minting *temporary MySQL login credentials*
|
|
@@ -5011,8 +5067,7 @@ async function generateMysqlCredential(options = {}) {
|
|
|
5011
5067
|
verifier: await mysqlNativePasswordVerifier(password)
|
|
5012
5068
|
};
|
|
5013
5069
|
}
|
|
5014
|
-
|
|
5015
|
-
//#region ../../packages/crypto/src/passphrase.ts
|
|
5070
|
+
// ../../packages/crypto/src/passphrase.ts
|
|
5016
5071
|
/**
|
|
5017
5072
|
* User private keys are stored server-side encrypted under a key derived from
|
|
5018
5073
|
* the user's passphrase, so any browser or CLI session can fetch and unlock
|
|
@@ -5071,8 +5126,7 @@ async function decryptPrivateKey(passphrase, blob) {
|
|
|
5071
5126
|
throw new SeekritCryptoError("DECRYPT_FAILED", "wrong passphrase or corrupted key blob");
|
|
5072
5127
|
}
|
|
5073
5128
|
}
|
|
5074
|
-
|
|
5075
|
-
//#region ../../packages/crypto/src/policy-key.ts
|
|
5129
|
+
// ../../packages/crypto/src/policy-key.ts
|
|
5076
5130
|
/**
|
|
5077
5131
|
* Signing with a principal's **existing** keypair, for agent access policy.
|
|
5078
5132
|
*
|
|
@@ -5141,8 +5195,7 @@ function policySignerJwk(publicKeyJwk) {
|
|
|
5141
5195
|
y: jwk.y
|
|
5142
5196
|
};
|
|
5143
5197
|
}
|
|
5144
|
-
|
|
5145
|
-
//#region ../../packages/crypto/src/shamir.ts
|
|
5198
|
+
// ../../packages/crypto/src/shamir.ts
|
|
5146
5199
|
/**
|
|
5147
5200
|
* Shamir's Secret Sharing over GF(2^8) — the same field AES uses, with the
|
|
5148
5201
|
* reduction polynomial x^8 + x^4 + x^3 + x + 1 (0x11b). Splits a byte string
|
|
@@ -5271,8 +5324,7 @@ function combineSecret(shares) {
|
|
|
5271
5324
|
}
|
|
5272
5325
|
return secret;
|
|
5273
5326
|
}
|
|
5274
|
-
|
|
5275
|
-
//#region ../../packages/crypto/src/recovery.ts
|
|
5327
|
+
// ../../packages/crypto/src/recovery.ts
|
|
5276
5328
|
/** Generate a fresh org recovery keypair (P-256, same as any principal). */
|
|
5277
5329
|
function generateRecoveryKey() {
|
|
5278
5330
|
return generateKeyPair();
|
|
@@ -5326,8 +5378,7 @@ async function combineRecoveryShares(shareBytes) {
|
|
|
5326
5378
|
throw new SeekritCryptoError("DECRYPT_FAILED", "recovery reconstruction failed: wrong, insufficient, or mismatched shares");
|
|
5327
5379
|
}
|
|
5328
5380
|
}
|
|
5329
|
-
|
|
5330
|
-
//#region ../../packages/crypto/src/redis.ts
|
|
5381
|
+
// ../../packages/crypto/src/redis.ts
|
|
5331
5382
|
/**
|
|
5332
5383
|
* Client-side construction of a Redis (6+) ACL password verifier, for minting
|
|
5333
5384
|
* *temporary Redis login credentials* without the password plaintext ever
|
|
@@ -5428,8 +5479,7 @@ async function generatePostgresCredential(options = {}) {
|
|
|
5428
5479
|
iterations
|
|
5429
5480
|
};
|
|
5430
5481
|
}
|
|
5431
|
-
|
|
5432
|
-
//#region ../../packages/crypto/src/sign.ts
|
|
5482
|
+
// ../../packages/crypto/src/sign.ts
|
|
5433
5483
|
/**
|
|
5434
5484
|
* Managed signing keys for the client-side KMS — ECDSA over P-256 (the curve
|
|
5435
5485
|
* already used for principal keypairs; universal in WebCrypto). A `sign` key's
|
|
@@ -5491,8 +5541,7 @@ function signatureKeyRef(signature) {
|
|
|
5491
5541
|
version
|
|
5492
5542
|
};
|
|
5493
5543
|
}
|
|
5494
|
-
|
|
5495
|
-
//#region ../../packages/crypto/src/ssh.ts
|
|
5544
|
+
// ../../packages/crypto/src/ssh.ts
|
|
5496
5545
|
/**
|
|
5497
5546
|
* Client-side SSH certificate authority for minting *temporary SSH access*
|
|
5498
5547
|
* without any private key ever reaching seekrit's control plane.
|
|
@@ -5623,8 +5672,7 @@ function encodeOpensshPrivateKey(seed, pub, comment) {
|
|
|
5623
5672
|
const b64 = toBase64(new SshWriter().bytes(utf8Encode("openssh-key-v1")).byte(0).string("none").string("none").string(/* @__PURE__ */ new Uint8Array(0)).uint32(1).string(ed25519PublicKeyBlob(pub)).string(concat(privSection, pad)).build());
|
|
5624
5673
|
return `-----BEGIN OPENSSH PRIVATE KEY-----\n${b64.match(/.{1,70}/g)?.join("\n") ?? b64}\n-----END OPENSSH PRIVATE KEY-----\n`;
|
|
5625
5674
|
}
|
|
5626
|
-
|
|
5627
|
-
//#region ../../packages/crypto/src/token.ts
|
|
5675
|
+
// ../../packages/crypto/src/token.ts
|
|
5628
5676
|
/**
|
|
5629
5677
|
* Service tokens (CI, docker builds, agent proxies, k8s, …) are self-contained
|
|
5630
5678
|
* principals. One local bundle holds independent authentication and decryption
|
|
@@ -5734,11 +5782,9 @@ async function createAgentTaskToken() {
|
|
|
5734
5782
|
tokenHash: await hashToken(token)
|
|
5735
5783
|
};
|
|
5736
5784
|
}
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
//#endregion
|
|
5741
|
-
//#region ../../packages/api-client/src/index.ts
|
|
5785
|
+
// package.json
|
|
5786
|
+
var version = "2.2.1";
|
|
5787
|
+
// ../../packages/api-client/src/index.ts
|
|
5742
5788
|
/**
|
|
5743
5789
|
* The proposer's note travels as a header, not a body field: an unprotected
|
|
5744
5790
|
* environment ignores it, so the same call works either way and no caller has
|
|
@@ -6735,8 +6781,7 @@ function writeProjectConfig(config, dir = process.cwd()) {
|
|
|
6735
6781
|
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`);
|
|
6736
6782
|
return path;
|
|
6737
6783
|
}
|
|
6738
|
-
|
|
6739
|
-
//#region src/io.ts
|
|
6784
|
+
// src/io.ts
|
|
6740
6785
|
let failThrows = false;
|
|
6741
6786
|
/**
|
|
6742
6787
|
* In `seekrit mcp` the process is a long-lived stdio server, so a `fail()`
|
|
@@ -6861,8 +6906,7 @@ async function readStdin() {
|
|
|
6861
6906
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
6862
6907
|
return Buffer.concat(chunks).toString("utf8");
|
|
6863
6908
|
}
|
|
6864
|
-
|
|
6865
|
-
//#region src/context.ts
|
|
6909
|
+
// src/context.ts
|
|
6866
6910
|
/** Sent as `x-seekrit-client` so the API attributes usage to the CLI (analytics). */
|
|
6867
6911
|
const CLI_CLIENT = `cli/${version}`;
|
|
6868
6912
|
/**
|
|
@@ -7002,8 +7046,7 @@ async function getDek(ctx, orgId, envId) {
|
|
|
7002
7046
|
const [{ wrappedDek }, privateKey] = await Promise.all([ctx.client.getMyEnvKey(orgId, envId), getPrivateKey(ctx)]);
|
|
7003
7047
|
return unwrapDek(wrappedDek, privateKey);
|
|
7004
7048
|
}
|
|
7005
|
-
|
|
7006
|
-
//#region src/output.ts
|
|
7049
|
+
// src/output.ts
|
|
7007
7050
|
/** Shorthand for a column whose header is fixed and value is one field. */
|
|
7008
7051
|
function col(header, value) {
|
|
7009
7052
|
return {
|
|
@@ -7065,8 +7108,7 @@ function section(title) {
|
|
|
7065
7108
|
console.log("");
|
|
7066
7109
|
console.log(title);
|
|
7067
7110
|
}
|
|
7068
|
-
|
|
7069
|
-
//#region src/target.ts
|
|
7111
|
+
// src/target.ts
|
|
7070
7112
|
/** Resolve the target org from a flag, the committed config, or a lone org. */
|
|
7071
7113
|
async function resolveOrg(ctx, orgSlug) {
|
|
7072
7114
|
const wanted = orgSlug ?? findProjectConfig()?.org;
|
|
@@ -7222,8 +7264,7 @@ async function resolveGroup(ctx, opts) {
|
|
|
7222
7264
|
slug: group.slug
|
|
7223
7265
|
};
|
|
7224
7266
|
}
|
|
7225
|
-
|
|
7226
|
-
//#region src/access.ts
|
|
7267
|
+
// src/access.ts
|
|
7227
7268
|
/**
|
|
7228
7269
|
* Human-readable labels for the principals holding a key grant. `grant list`
|
|
7229
7270
|
* would otherwise be a column of opaque ids, which is exactly the question
|
|
@@ -7316,8 +7357,7 @@ function registerAccessCommands(program) {
|
|
|
7316
7357
|
console.error(`revoked ${options.user ?? options.token}'s access to ${label}`);
|
|
7317
7358
|
});
|
|
7318
7359
|
}
|
|
7319
|
-
|
|
7320
|
-
//#region src/account.ts
|
|
7360
|
+
// src/account.ts
|
|
7321
7361
|
function sessionStatus(session) {
|
|
7322
7362
|
if (session.revokedAt) return "revoked";
|
|
7323
7363
|
if (session.expiresAt && Date.parse(session.expiresAt) < Date.now()) return "expired";
|
|
@@ -7367,8 +7407,7 @@ function registerAccountCommands(program) {
|
|
|
7367
7407
|
console.error(`${type} emails ${state}`);
|
|
7368
7408
|
});
|
|
7369
7409
|
}
|
|
7370
|
-
|
|
7371
|
-
//#region src/agents.ts
|
|
7410
|
+
// src/agents.ts
|
|
7372
7411
|
/**
|
|
7373
7412
|
* Agent access governance from the CLI.
|
|
7374
7413
|
*
|
|
@@ -7500,6 +7539,14 @@ function describeList(values) {
|
|
|
7500
7539
|
function describeSecrets(values) {
|
|
7501
7540
|
return values.length === 0 ? "none" : values.join(" ");
|
|
7502
7541
|
}
|
|
7542
|
+
/**
|
|
7543
|
+
* The third convention, and the one most likely to be misread: **no ports means
|
|
7544
|
+
* HTTPS on 443**, not any port. A blank or `any` here would say the opposite of
|
|
7545
|
+
* what the proxy enforces, so the default is spelled out.
|
|
7546
|
+
*/
|
|
7547
|
+
function describePorts(rule) {
|
|
7548
|
+
return describePolicyPorts(rule) ?? "https:443";
|
|
7549
|
+
}
|
|
7503
7550
|
function printRules(rules) {
|
|
7504
7551
|
printTable(rules.map((rule, i) => ({
|
|
7505
7552
|
n: i + 1,
|
|
@@ -7507,6 +7554,7 @@ function printRules(rules) {
|
|
|
7507
7554
|
})), [
|
|
7508
7555
|
col("#", (r) => r.n),
|
|
7509
7556
|
col("host", (r) => r.rule.host),
|
|
7557
|
+
col("ports", (r) => describePorts(r.rule)),
|
|
7510
7558
|
col("methods", (r) => describeList(r.rule.methods)),
|
|
7511
7559
|
col("paths", (r) => describeList(r.rule.paths)),
|
|
7512
7560
|
col("secrets", (r) => describeSecrets(r.rule.allow)),
|
|
@@ -7515,8 +7563,9 @@ function printRules(rules) {
|
|
|
7515
7563
|
}
|
|
7516
7564
|
/** One rule as a single line, for the publish diff. */
|
|
7517
7565
|
function ruleLine(rule) {
|
|
7566
|
+
const ports = describePolicyPorts(rule);
|
|
7518
7567
|
const parts = [
|
|
7519
|
-
rule.host,
|
|
7568
|
+
ports ? `${rule.host} [${ports}]` : rule.host,
|
|
7520
7569
|
describeList(rule.methods),
|
|
7521
7570
|
describeList(rule.paths),
|
|
7522
7571
|
`secrets=${describeSecrets(rule.allow)}`
|
|
@@ -7843,7 +7892,11 @@ function registerAgentCommands(program) {
|
|
|
7843
7892
|
console.error("widening proposals are never applied: an agent must not earn permissions by retrying");
|
|
7844
7893
|
}
|
|
7845
7894
|
});
|
|
7846
|
-
agents.command("simulate <ref>").description("ask what a policy would decide, without sending a request").requiredOption("--host <host>", "upstream hostname").requiredOption("--path <path>", "request path").option("--method <method>", "HTTP method", "GET").option("--secret <NAME>", "the secret the request would inject").option("--version <n>", "evaluate a specific published version").option("--org <slug>", "organization").option("--json", "machine-readable output").action(async (ref, options) => {
|
|
7895
|
+
agents.command("simulate <ref>").description("ask what a policy would decide, without sending a request").requiredOption("--host <host>", "upstream hostname").requiredOption("--path <path>", "request path").option("--method <method>", "HTTP method", "GET").option("--scheme <scheme>", "https or http, as the agent dials it (forward proxy)", "https").option("--port <n>", "port the agent dials (forward proxy; default 443 for https, 80 for http)").option("--secret <NAME>", "the secret the request would inject").option("--version <n>", "evaluate a specific published version").option("--org <slug>", "organization").option("--json", "machine-readable output").action(async (ref, options) => {
|
|
7896
|
+
const scheme = options.scheme.toLowerCase();
|
|
7897
|
+
if (scheme !== "https" && scheme !== "http") fail("--scheme must be https or http");
|
|
7898
|
+
const port = options.port === void 0 ? scheme === "https" ? 443 : 80 : Number(options.port);
|
|
7899
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) fail("--port must be 1-65535");
|
|
7847
7900
|
const ctx = buildContext();
|
|
7848
7901
|
const org = await resolveOrg(ctx, options.org);
|
|
7849
7902
|
const agent = await resolveAgent(ctx, org.id, ref);
|
|
@@ -7853,6 +7906,10 @@ function registerAgentCommands(program) {
|
|
|
7853
7906
|
if (!published) fail(`${agent.slug} has no published policy — every request is denied`);
|
|
7854
7907
|
const verdict = evaluatePolicy(published.rules, {
|
|
7855
7908
|
host: options.host,
|
|
7909
|
+
transport: {
|
|
7910
|
+
scheme,
|
|
7911
|
+
port
|
|
7912
|
+
},
|
|
7856
7913
|
method: options.method,
|
|
7857
7914
|
path: options.path,
|
|
7858
7915
|
...options.secret === void 0 ? {} : { secret: options.secret }
|
|
@@ -7863,7 +7920,7 @@ function registerAgentCommands(program) {
|
|
|
7863
7920
|
verdict,
|
|
7864
7921
|
reason
|
|
7865
7922
|
}, () => {
|
|
7866
|
-
const target = `${options.method.toUpperCase()} ${options.host}${options.path}`;
|
|
7923
|
+
const target = `${options.method.toUpperCase()} ${scheme}://${options.host}:${port}${options.path}`;
|
|
7867
7924
|
console.log(`${verdict.decision === "allow" ? "allow" : "DENY"} ${target}${options.secret ? ` [${options.secret}]` : ""}`);
|
|
7868
7925
|
console.log(` v${published.version}: ${reason}`);
|
|
7869
7926
|
});
|
|
@@ -7943,7 +8000,7 @@ function registerPolicyCommands(policy) {
|
|
|
7943
8000
|
const { signingKey, jwk } = await getPolicySigner(ctx);
|
|
7944
8001
|
const issuedAt = Math.floor(Date.now() / 1e3);
|
|
7945
8002
|
const bundle = await signAgentPolicy(signingKey, jwk, {
|
|
7946
|
-
v:
|
|
8003
|
+
v: policyBundleVersionFor(rules),
|
|
7947
8004
|
org: org.id,
|
|
7948
8005
|
agent: agent.id,
|
|
7949
8006
|
agent_slug: agent.slug,
|
|
@@ -8356,8 +8413,7 @@ function printExplain(provenance, refs = {}) {
|
|
|
8356
8413
|
}
|
|
8357
8414
|
if (refs.unresolved?.length) process.stderr.write(`\nunresolved reference(s), left as literal text: ${refs.unresolved.join(", ")}\n`);
|
|
8358
8415
|
}
|
|
8359
|
-
|
|
8360
|
-
//#region src/env-diff.ts
|
|
8416
|
+
// src/env-diff.ts
|
|
8361
8417
|
/**
|
|
8362
8418
|
* The comparison itself, kept pure so it can be tested without a network or a
|
|
8363
8419
|
* key. Rows come back sorted by name: a diff people read top-to-bottom should
|
|
@@ -8474,8 +8530,7 @@ function registerEnvDiff(env) {
|
|
|
8474
8530
|
});
|
|
8475
8531
|
});
|
|
8476
8532
|
}
|
|
8477
|
-
|
|
8478
|
-
//#region src/group-sync-grants.ts
|
|
8533
|
+
// src/group-sync-grants.ts
|
|
8479
8534
|
/** Wrap a newly composed group layer for connections already syncing the app env or its branches. */
|
|
8480
8535
|
async function syncGrantsForGroupLink(ctx, orgId, envId, envSlug, groupId) {
|
|
8481
8536
|
const [{ groups }, { bindings }, { branches }] = await Promise.all([
|
|
@@ -8500,8 +8555,7 @@ async function syncGrantsForGroupLink(ctx, orgId, envId, envSlug, groupId) {
|
|
|
8500
8555
|
};
|
|
8501
8556
|
}));
|
|
8502
8557
|
}
|
|
8503
|
-
|
|
8504
|
-
//#region src/kms.ts
|
|
8558
|
+
// src/kms.ts
|
|
8505
8559
|
/** Collect a repeatable option into a list. */
|
|
8506
8560
|
function collect$9(value, acc = []) {
|
|
8507
8561
|
acc.push(value);
|
|
@@ -8790,8 +8844,7 @@ function registerKmsCommands(program) {
|
|
|
8790
8844
|
}
|
|
8791
8845
|
});
|
|
8792
8846
|
}
|
|
8793
|
-
|
|
8794
|
-
//#region src/recovery.ts
|
|
8847
|
+
// src/recovery.ts
|
|
8795
8848
|
/** Collect a repeatable option into a list. */
|
|
8796
8849
|
function collect$8(value, acc = []) {
|
|
8797
8850
|
acc.push(value);
|
|
@@ -8990,8 +9043,7 @@ function registerRecoveryCommands(program) {
|
|
|
8990
9043
|
console.error(`recovery request ${requestId} canceled`);
|
|
8991
9044
|
});
|
|
8992
9045
|
}
|
|
8993
|
-
|
|
8994
|
-
//#region src/apps.ts
|
|
9046
|
+
// src/apps.ts
|
|
8995
9047
|
/**
|
|
8996
9048
|
* Applications and their environments — the structure everything else hangs
|
|
8997
9049
|
* off. Creating an environment is the one command here that does crypto: the
|
|
@@ -9193,8 +9245,7 @@ function registerAppCommands(program) {
|
|
|
9193
9245
|
console.error(`removed ${group.slug} from ${target.appSlug}/${target.envSlug}`);
|
|
9194
9246
|
});
|
|
9195
9247
|
}
|
|
9196
|
-
|
|
9197
|
-
//#region src/decryptor.ts
|
|
9248
|
+
// src/decryptor.ts
|
|
9198
9249
|
/**
|
|
9199
9250
|
* The standalone offline decryptor: one self-contained HTML file, written out by
|
|
9200
9251
|
* `seekrit archive decryptor`, that opens a break-glass archive in any browser
|
|
@@ -10026,8 +10077,7 @@ const OFFLINE_DECRYPTOR_HTML = `<!doctype html>
|
|
|
10026
10077
|
</body>
|
|
10027
10078
|
</html>
|
|
10028
10079
|
`;
|
|
10029
|
-
|
|
10030
|
-
//#region src/format.ts
|
|
10080
|
+
// src/format.ts
|
|
10031
10081
|
function shellQuote(value) {
|
|
10032
10082
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
10033
10083
|
}
|
|
@@ -10039,8 +10089,7 @@ function formatSecrets(values, format) {
|
|
|
10039
10089
|
case "dotenv": return names.map((name) => `${name}=${dotenvQuote(values[name] ?? "")}`).join("\n");
|
|
10040
10090
|
}
|
|
10041
10091
|
}
|
|
10042
|
-
|
|
10043
|
-
//#region src/archive.ts
|
|
10092
|
+
// src/archive.ts
|
|
10044
10093
|
/**
|
|
10045
10094
|
* Break-glass archives (docs/break-glass-export.md).
|
|
10046
10095
|
*
|
|
@@ -10370,8 +10419,7 @@ function registerArchiveCommands(program) {
|
|
|
10370
10419
|
console.error("open it in any browser — it declares a Content-Security-Policy of `default-src 'none'`, so it cannot reach the network.");
|
|
10371
10420
|
});
|
|
10372
10421
|
}
|
|
10373
|
-
|
|
10374
|
-
//#region src/audit.ts
|
|
10422
|
+
// src/audit.ts
|
|
10375
10423
|
/** The API's per-page ceiling (`auditQuerySchema.limit`). */
|
|
10376
10424
|
const MAX_PAGE = 200;
|
|
10377
10425
|
function parseLimit(raw) {
|
|
@@ -10425,8 +10473,7 @@ function registerAuditCommands(program) {
|
|
|
10425
10473
|
});
|
|
10426
10474
|
});
|
|
10427
10475
|
}
|
|
10428
|
-
|
|
10429
|
-
//#region src/aws.ts
|
|
10476
|
+
// src/aws.ts
|
|
10430
10477
|
/**
|
|
10431
10478
|
* `seekrit aws` — temporary AWS credentials via STS AssumeRole (Vault-style
|
|
10432
10479
|
* dynamic secrets, the tier-2 sibling of `seekrit pg`).
|
|
@@ -10568,8 +10615,7 @@ function registerAwsCommands(program) {
|
|
|
10568
10615
|
console.error(`revoked ${leaseId} (issued credentials remain valid until they expire)`);
|
|
10569
10616
|
});
|
|
10570
10617
|
}
|
|
10571
|
-
|
|
10572
|
-
//#region src/billing.ts
|
|
10618
|
+
// src/billing.ts
|
|
10573
10619
|
/** `included: null` means unlimited; an unavailable meter is unknown, not zero. */
|
|
10574
10620
|
function usageLine(usage) {
|
|
10575
10621
|
if (!usage.available) return "unknown";
|
|
@@ -10644,8 +10690,7 @@ function registerBillingCommands(program) {
|
|
|
10644
10690
|
console.error(`${ref.slug} moved to the Free plan`);
|
|
10645
10691
|
});
|
|
10646
10692
|
}
|
|
10647
|
-
|
|
10648
|
-
//#region src/branches.ts
|
|
10693
|
+
// src/branches.ts
|
|
10649
10694
|
/**
|
|
10650
10695
|
* Resolve `--from` to the environment being branched.
|
|
10651
10696
|
*
|
|
@@ -10742,8 +10787,7 @@ function registerBranchCommands(program) {
|
|
|
10742
10787
|
console.error(`deleted branch ${app.slug}#${target.slug}`);
|
|
10743
10788
|
});
|
|
10744
10789
|
}
|
|
10745
|
-
|
|
10746
|
-
//#region src/changes.ts
|
|
10790
|
+
// src/changes.ts
|
|
10747
10791
|
/**
|
|
10748
10792
|
* Change requests: the review queue in front of protected environments, and the
|
|
10749
10793
|
* protection settings that create it.
|
|
@@ -11271,8 +11315,7 @@ async function resolveEnvironment(ctx, scope) {
|
|
|
11271
11315
|
});
|
|
11272
11316
|
return values;
|
|
11273
11317
|
}
|
|
11274
|
-
|
|
11275
|
-
//#region src/doctor.ts
|
|
11318
|
+
// src/doctor.ts
|
|
11276
11319
|
/** Every `SEEKRIT_*` variable the CLI reads, for the set-but-blank check. */
|
|
11277
11320
|
const READ_VARS = [
|
|
11278
11321
|
"SEEKRIT_TOKEN",
|
|
@@ -11692,8 +11735,7 @@ function registerDoctorCommand(program) {
|
|
|
11692
11735
|
if (checks.some((c) => c.status === "fail")) process.exitCode = 1;
|
|
11693
11736
|
});
|
|
11694
11737
|
}
|
|
11695
|
-
|
|
11696
|
-
//#region src/gcp.ts
|
|
11738
|
+
// src/gcp.ts
|
|
11697
11739
|
/**
|
|
11698
11740
|
* `seekrit gcp` — temporary GCP credentials via IAM Credentials
|
|
11699
11741
|
* `generateAccessToken` (Vault-style dynamic secrets, the tier-2 sibling of
|
|
@@ -11835,8 +11877,7 @@ function registerGcpCommands(program) {
|
|
|
11835
11877
|
console.error(`revoked ${leaseId} (issued tokens remain valid until they expire)`);
|
|
11836
11878
|
});
|
|
11837
11879
|
}
|
|
11838
|
-
|
|
11839
|
-
//#region src/groups.ts
|
|
11880
|
+
// src/groups.ts
|
|
11840
11881
|
/**
|
|
11841
11882
|
* Shared groups: reusable secret bags composed into application environments
|
|
11842
11883
|
* by matching slug (`seekrit env groups add`). A group holds one environment
|
|
@@ -11943,8 +11984,7 @@ function registerGroupCommands(program) {
|
|
|
11943
11984
|
console.error(`created ${groupRef.slug}@${created.environment.slug} (${created.environment.id})`);
|
|
11944
11985
|
});
|
|
11945
11986
|
}
|
|
11946
|
-
|
|
11947
|
-
//#region src/hermes.ts
|
|
11987
|
+
// src/hermes.ts
|
|
11948
11988
|
/**
|
|
11949
11989
|
* `seekrit hermes` — wire seekrit into [Hermes Agent](https://hermes-agent.nousresearch.com)
|
|
11950
11990
|
* as a **secret source**.
|
|
@@ -12161,8 +12201,7 @@ function registerHermesCommands(program) {
|
|
|
12161
12201
|
emit(options, { reference: ref }, () => console.log(ref));
|
|
12162
12202
|
});
|
|
12163
12203
|
}
|
|
12164
|
-
|
|
12165
|
-
//#region src/honey.ts
|
|
12204
|
+
// src/honey.ts
|
|
12166
12205
|
/**
|
|
12167
12206
|
* Honey tokens — decoy credentials that unlock nothing and alert when used.
|
|
12168
12207
|
*
|
|
@@ -12207,8 +12246,7 @@ function registerHoneyTokenCommands(program) {
|
|
|
12207
12246
|
console.error(`${honeyTokenId} deleted`);
|
|
12208
12247
|
});
|
|
12209
12248
|
}
|
|
12210
|
-
|
|
12211
|
-
//#region src/m2m.ts
|
|
12249
|
+
// src/m2m.ts
|
|
12212
12250
|
/**
|
|
12213
12251
|
* Resolve M2M client credentials from (in order) the process environment, a
|
|
12214
12252
|
* `.env` overlay (for `seekrit run`), then saved config. Both halves must come
|
|
@@ -12445,8 +12483,7 @@ function registerInitCommand(program) {
|
|
|
12445
12483
|
await runInit(options);
|
|
12446
12484
|
});
|
|
12447
12485
|
}
|
|
12448
|
-
|
|
12449
|
-
//#region src/logsink.ts
|
|
12486
|
+
// src/logsink.ts
|
|
12450
12487
|
/** Collect repeated `--header Name: value` flags into a map. */
|
|
12451
12488
|
function collectHeader(value, acc = {}) {
|
|
12452
12489
|
const sep = value.indexOf(":");
|
|
@@ -12513,8 +12550,7 @@ function registerLogSinkCommands(program) {
|
|
|
12513
12550
|
console.error("log sink removed");
|
|
12514
12551
|
});
|
|
12515
12552
|
}
|
|
12516
|
-
|
|
12517
|
-
//#region src/mongodb.ts
|
|
12553
|
+
// src/mongodb.ts
|
|
12518
12554
|
/**
|
|
12519
12555
|
* `seekrit mongodb` — temporary MongoDB credentials (Vault-style dynamic
|
|
12520
12556
|
* secrets), the tier-2 sibling of `seekrit aws`.
|
|
@@ -12648,8 +12684,7 @@ function registerMongoCommands(program) {
|
|
|
12648
12684
|
console.error(`revoked ${leaseId} (the MongoDB user has been dropped)`);
|
|
12649
12685
|
});
|
|
12650
12686
|
}
|
|
12651
|
-
|
|
12652
|
-
//#region src/provisioner.ts
|
|
12687
|
+
// src/provisioner.ts
|
|
12653
12688
|
/**
|
|
12654
12689
|
* `seekrit provisioner` — helpers for the self-hosted **remote executor**
|
|
12655
12690
|
* (`seekrit-provisioner`), the daemon that runs a target's provisioning SQL
|
|
@@ -12691,8 +12726,7 @@ function resolveLeaseAdminSecret(opts) {
|
|
|
12691
12726
|
if (!adminUrl) fail(`provide the admin connection string via --admin-url or ${opts.adminUrlEnv}`);
|
|
12692
12727
|
return adminUrl;
|
|
12693
12728
|
}
|
|
12694
|
-
|
|
12695
|
-
//#region src/mysql.ts
|
|
12729
|
+
// src/mysql.ts
|
|
12696
12730
|
/**
|
|
12697
12731
|
* `seekrit mysql` — temporary MySQL/MariaDB credentials (Vault-style dynamic
|
|
12698
12732
|
* secrets).
|
|
@@ -12832,8 +12866,7 @@ function collect$5(value, acc) {
|
|
|
12832
12866
|
acc.push(value);
|
|
12833
12867
|
return acc;
|
|
12834
12868
|
}
|
|
12835
|
-
|
|
12836
|
-
//#region src/proxy-binary.ts
|
|
12869
|
+
// src/proxy-binary.ts
|
|
12837
12870
|
/**
|
|
12838
12871
|
* Fetch and run the `seekrit-proxy` binary without a Rust toolchain.
|
|
12839
12872
|
*
|
|
@@ -12868,7 +12901,7 @@ function collect$5(value, acc) {
|
|
|
12868
12901
|
* release-please-config.json), so the pin follows the crate without anyone
|
|
12869
12902
|
* remembering to move it.
|
|
12870
12903
|
*/
|
|
12871
|
-
const PROXY_VERSION = "0.
|
|
12904
|
+
const PROXY_VERSION = "0.14.0";
|
|
12872
12905
|
const BIN = "seekrit-proxy";
|
|
12873
12906
|
/**
|
|
12874
12907
|
* Host → Rust target triple.
|
|
@@ -12904,7 +12937,7 @@ function versionPrefix(version) {
|
|
|
12904
12937
|
return version.startsWith("v") ? version : `v${version}`;
|
|
12905
12938
|
}
|
|
12906
12939
|
function resolveVersion(explicit) {
|
|
12907
|
-
return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.
|
|
12940
|
+
return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.14.0";
|
|
12908
12941
|
}
|
|
12909
12942
|
function resolveBaseUrl(explicit) {
|
|
12910
12943
|
return (explicit ?? process.env.SEEKRIT_PROXY_BASE_URL ?? "https://proxy.seekrit.dev").replace(/\/+$/, "");
|
|
@@ -13014,8 +13047,7 @@ function signalNumber(signal) {
|
|
|
13014
13047
|
SIGTERM: 15
|
|
13015
13048
|
}[signal] ?? 0;
|
|
13016
13049
|
}
|
|
13017
|
-
|
|
13018
|
-
//#region src/nomad.ts
|
|
13050
|
+
// src/nomad.ts
|
|
13019
13051
|
/**
|
|
13020
13052
|
* `seekrit nomad` — wire seekrit into [HashiCorp Nomad](https://developer.hashicorp.com/nomad)
|
|
13021
13053
|
* as a **secret provider plugin**.
|
|
@@ -13399,8 +13431,7 @@ function registerNomadCommands(program) {
|
|
|
13399
13431
|
function indent(text) {
|
|
13400
13432
|
return text.split("\n").map((line) => line.length > 0 ? ` ${line}` : line).join("\n");
|
|
13401
13433
|
}
|
|
13402
|
-
|
|
13403
|
-
//#region src/orgs.ts
|
|
13434
|
+
// src/orgs.ts
|
|
13404
13435
|
/**
|
|
13405
13436
|
* Counts for `org show`. Each list is admin-gated to a different degree, so a
|
|
13406
13437
|
* member who can't read one still gets the rest: a failed call reports as
|
|
@@ -13557,8 +13588,7 @@ function registerOrgCommands(program) {
|
|
|
13557
13588
|
});
|
|
13558
13589
|
});
|
|
13559
13590
|
}
|
|
13560
|
-
|
|
13561
|
-
//#region src/proxy-presets.ts
|
|
13591
|
+
// src/proxy-presets.ts
|
|
13562
13592
|
/** `Authorization: Bearer {{seekrit:NAME}}` — the shape most providers take. */
|
|
13563
13593
|
function placeholder(secret) {
|
|
13564
13594
|
return `{{seekrit:${secret}}}`;
|
|
@@ -13742,8 +13772,31 @@ function presetAllow(preset) {
|
|
|
13742
13772
|
if (preset.allow) return preset.allow;
|
|
13743
13773
|
return preset.secret ? [preset.secret] : [];
|
|
13744
13774
|
}
|
|
13745
|
-
|
|
13746
|
-
|
|
13775
|
+
// src/proxy-config.ts
|
|
13776
|
+
/**
|
|
13777
|
+
* Generate a `seekrit-proxy.toml` — the other half of "adoptable without Rust".
|
|
13778
|
+
*
|
|
13779
|
+
* `npx @seekrit/proxy` removes the toolchain; this removes the config file. The
|
|
13780
|
+
* proxy's TOML is not large, but it is the kind of file where every field has to
|
|
13781
|
+
* be right at once before anything works: a bare hostname in one place and a
|
|
13782
|
+
* base URL in another, a path glob matched against the *upstream-facing* path,
|
|
13783
|
+
* a trust anchor that must not come from us, and two modes whose blocks look
|
|
13784
|
+
* similar and mean different things. Generating it turns first-run from an
|
|
13785
|
+
* exercise in reading reference docs into a command.
|
|
13786
|
+
*
|
|
13787
|
+
* Two things this module is careful about:
|
|
13788
|
+
*
|
|
13789
|
+
* - **It writes the config the proxy actually accepts.** In server-policy mode
|
|
13790
|
+
* `allow`/`methods`/`paths` on a route are *rejected*, not ignored, and the
|
|
13791
|
+
* forward proxy's hosts come from the bundle — so the server-mode emitter
|
|
13792
|
+
* leaves all four out rather than emitting a file that fails at startup. The
|
|
13793
|
+
* golden fixtures in `apps/proxy/testdata/generated-configs/` are parsed by a
|
|
13794
|
+
* Rust test, so this cannot drift from the parser.
|
|
13795
|
+
* - **It never pretends a fetched thumbprint is a local trust anchor.** The
|
|
13796
|
+
* `signers` list is the one field that must not come from the API. What is
|
|
13797
|
+
* emitted is annotated with where it came from, and the generated file says
|
|
13798
|
+
* so, in the file, next to the value.
|
|
13799
|
+
*/
|
|
13747
13800
|
/**
|
|
13748
13801
|
* A deliberately small TOML writer: basic strings and arrays of them, which is
|
|
13749
13802
|
* every value in this config. Full TOML is not needed and a general emitter
|
|
@@ -13964,6 +14017,22 @@ function splitHostPort(addr) {
|
|
|
13964
14017
|
if (idx === -1) return [addr, "8080"];
|
|
13965
14018
|
return [addr.slice(0, idx), addr.slice(idx + 1)];
|
|
13966
14019
|
}
|
|
14020
|
+
/**
|
|
14021
|
+
* Where a policy rule's service actually is, as a reverse-proxy upstream.
|
|
14022
|
+
*
|
|
14023
|
+
* A rule names its ports (HTTPS on 443 unless it says otherwise), so a route
|
|
14024
|
+
* generated from it has to dial one of them — `https://host` for a rule that
|
|
14025
|
+
* only covers `http_ports: [5000]` would route to a service the rule never
|
|
14026
|
+
* mentioned. The route is routing, not authorization: the reverse plane never
|
|
14027
|
+
* refuses on ports, because this URL is the operator's to edit.
|
|
14028
|
+
*/
|
|
14029
|
+
function upstreamForRule(rule) {
|
|
14030
|
+
const { https, http } = policyRulePorts(rule);
|
|
14031
|
+
if (https.includes(443)) return `https://${rule.host}`;
|
|
14032
|
+
if (https.length > 0) return `https://${rule.host}:${https[0]}`;
|
|
14033
|
+
const port = http[0];
|
|
14034
|
+
return port === 80 ? `http://${rule.host}` : `http://${rule.host}:${port}`;
|
|
14035
|
+
}
|
|
13967
14036
|
/** Build a file-policy plan from presets and/or ad-hoc `host=SECRET` rules. */
|
|
13968
14037
|
function planFromPresets(presets, options) {
|
|
13969
14038
|
const taken = /* @__PURE__ */ new Set();
|
|
@@ -14039,7 +14108,7 @@ function planFromPolicy(args, options) {
|
|
|
14039
14108
|
const baseUrl = baseUrlFor(options.listen, prefix, preset?.baseUrlSuffix ?? "");
|
|
14040
14109
|
routes.push({
|
|
14041
14110
|
prefix,
|
|
14042
|
-
upstream:
|
|
14111
|
+
upstream: upstreamForRule(rule),
|
|
14043
14112
|
host: rule.host,
|
|
14044
14113
|
allow: [],
|
|
14045
14114
|
methods: [],
|
|
@@ -14182,8 +14251,7 @@ function renderComposeSnippet(plan, options) {
|
|
|
14182
14251
|
out.push(forward ? "# `internal: true` network with the proxy as its only peer." : "# on an `internal: true` network with the proxy as its only peer.");
|
|
14183
14252
|
return `${out.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}\n`;
|
|
14184
14253
|
}
|
|
14185
|
-
|
|
14186
|
-
//#region src/paperclip.ts
|
|
14254
|
+
// src/paperclip.ts
|
|
14187
14255
|
/**
|
|
14188
14256
|
* `seekrit paperclip` — wire seekrit into a [Paperclip](https://docs.paperclip.ing)
|
|
14189
14257
|
* agent.
|
|
@@ -14371,8 +14439,7 @@ function registerPaperclipCommands(program) {
|
|
|
14371
14439
|
});
|
|
14372
14440
|
});
|
|
14373
14441
|
}
|
|
14374
|
-
|
|
14375
|
-
//#region src/pg.ts
|
|
14442
|
+
// src/pg.ts
|
|
14376
14443
|
/**
|
|
14377
14444
|
* `seekrit pg` — temporary Postgres credentials (Vault-style dynamic secrets).
|
|
14378
14445
|
*
|
|
@@ -14524,8 +14591,7 @@ function collect$3(value, acc) {
|
|
|
14524
14591
|
acc.push(value);
|
|
14525
14592
|
return acc;
|
|
14526
14593
|
}
|
|
14527
|
-
|
|
14528
|
-
//#region src/proxy.ts
|
|
14594
|
+
// src/proxy.ts
|
|
14529
14595
|
/**
|
|
14530
14596
|
* `seekrit proxy` — get `seekrit-proxy` running without a Rust toolchain or a
|
|
14531
14597
|
* hand-written TOML file.
|
|
@@ -14854,10 +14920,10 @@ Next:
|
|
|
14854
14920
|
});
|
|
14855
14921
|
console.log(path);
|
|
14856
14922
|
const { target } = detectTarget();
|
|
14857
|
-
process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.
|
|
14923
|
+
process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.14.0")} (${target})\n`);
|
|
14858
14924
|
});
|
|
14859
14925
|
proxy.command("where").description("show which binary `seekrit proxy run` would use, without fetching it").option("--proxy-version <version>", `version to report (default: ${PROXY_VERSION})`).option("--json", "machine-readable output").action((options) => {
|
|
14860
|
-
const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.
|
|
14926
|
+
const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.14.0";
|
|
14861
14927
|
const override = process.env.SEEKRIT_PROXY_BIN;
|
|
14862
14928
|
const { target, exe } = detectTarget();
|
|
14863
14929
|
const path = override ?? proxyBinaryPath(version, target, exe);
|
|
@@ -14916,13 +14982,12 @@ Next:
|
|
|
14916
14982
|
process.stdout.write(renderComposeSnippet(plan, {
|
|
14917
14983
|
service: options.service,
|
|
14918
14984
|
workload: options.workload,
|
|
14919
|
-
image: options.image ?? `seekritdev/proxy:0.
|
|
14985
|
+
image: options.image ?? `seekritdev/proxy:0.14.0`,
|
|
14920
14986
|
publish: Boolean(options.publish)
|
|
14921
14987
|
}));
|
|
14922
14988
|
});
|
|
14923
14989
|
}
|
|
14924
|
-
|
|
14925
|
-
//#region src/redis.ts
|
|
14990
|
+
// src/redis.ts
|
|
14926
14991
|
/**
|
|
14927
14992
|
* `seekrit redis` — temporary Redis (6+) credentials (Vault-style dynamic
|
|
14928
14993
|
* secrets).
|
|
@@ -15062,8 +15127,7 @@ function collect$2(value, acc) {
|
|
|
15062
15127
|
acc.push(value);
|
|
15063
15128
|
return acc;
|
|
15064
15129
|
}
|
|
15065
|
-
|
|
15066
|
-
//#region src/rotation.ts
|
|
15130
|
+
// src/rotation.ts
|
|
15067
15131
|
/**
|
|
15068
15132
|
* `seekrit rotation` — managed, scheduled rotation of a stored secret's value.
|
|
15069
15133
|
*
|
|
@@ -15230,8 +15294,7 @@ function registerRotationCommands(program) {
|
|
|
15230
15294
|
console.error(`disabled rotation of ${r.secretName}${rotatorRevoked ? " — rotator key access revoked for this environment" : ""}`);
|
|
15231
15295
|
});
|
|
15232
15296
|
}
|
|
15233
|
-
|
|
15234
|
-
//#region src/run-command.ts
|
|
15297
|
+
// src/run-command.ts
|
|
15235
15298
|
/** Keep signal handlers installed from provisioning through asynchronous cleanup. */
|
|
15236
15299
|
async function runCommand(cmd, args, prepare, cleanup) {
|
|
15237
15300
|
let child;
|
|
@@ -15401,8 +15464,7 @@ async function reapStragglers(pids, signal) {
|
|
|
15401
15464
|
process.kill(pid, "SIGKILL");
|
|
15402
15465
|
} catch {}
|
|
15403
15466
|
}
|
|
15404
|
-
|
|
15405
|
-
//#region src/run-leases.ts
|
|
15467
|
+
// src/run-leases.ts
|
|
15406
15468
|
/** Validate all flags before contacting the API or creating any database roles. */
|
|
15407
15469
|
function parseRunLeaseOptions(bindings = [], ttl) {
|
|
15408
15470
|
if (!bindings.length) {
|
|
@@ -15527,8 +15589,7 @@ var RunLeases = class {
|
|
|
15527
15589
|
return ok;
|
|
15528
15590
|
}
|
|
15529
15591
|
};
|
|
15530
|
-
|
|
15531
|
-
//#region src/ssh.ts
|
|
15592
|
+
// src/ssh.ts
|
|
15532
15593
|
/**
|
|
15533
15594
|
* `seekrit ssh` — temporary SSH access via short-lived certificates (Vault-style
|
|
15534
15595
|
* dynamic secrets, the SSH sibling of `seekrit pg`).
|
|
@@ -15670,8 +15731,7 @@ function collect$1(value, acc) {
|
|
|
15670
15731
|
acc.push(value);
|
|
15671
15732
|
return acc;
|
|
15672
15733
|
}
|
|
15673
|
-
|
|
15674
|
-
//#region src/sync.ts
|
|
15734
|
+
// src/sync.ts
|
|
15675
15735
|
/** Split a `--flag a,b` list into trimmed, non-empty members. */
|
|
15676
15736
|
function list(raw, fallback) {
|
|
15677
15737
|
return (raw ?? fallback).split(",").map((item) => item.trim()).filter(Boolean);
|
|
@@ -16417,8 +16477,7 @@ function registerSyncCommands(program) {
|
|
|
16417
16477
|
], "no sync runs yet"));
|
|
16418
16478
|
});
|
|
16419
16479
|
}
|
|
16420
|
-
|
|
16421
|
-
//#region src/web-login.ts
|
|
16480
|
+
// src/web-login.ts
|
|
16422
16481
|
/**
|
|
16423
16482
|
* `seekrit login` — sign in through the browser.
|
|
16424
16483
|
*
|
|
@@ -16593,8 +16652,7 @@ async function runLogout() {
|
|
|
16593
16652
|
});
|
|
16594
16653
|
if (config.clientId) console.error("(machine client credentials are kept — remove them with `seekrit login`)");
|
|
16595
16654
|
}
|
|
16596
|
-
|
|
16597
|
-
//#region src/index.ts
|
|
16655
|
+
// src/index.ts
|
|
16598
16656
|
/** Collect repeated `--with group=env` flags into a map. */
|
|
16599
16657
|
function collectKv(value, acc = {}) {
|
|
16600
16658
|
const eq = value.indexOf("=");
|
|
@@ -17045,7 +17103,7 @@ registerKmsCommands(program);
|
|
|
17045
17103
|
registerRotationCommands(program);
|
|
17046
17104
|
registerRecoveryCommands(program);
|
|
17047
17105
|
program.command("mcp").description("run an MCP server over stdio so AI agents can drive seekrit").action(async () => {
|
|
17048
|
-
const { runMcpServer } = await import("./mcp-
|
|
17106
|
+
const { runMcpServer } = await import("./mcp-Clul2n4l.js");
|
|
17049
17107
|
await runMcpServer();
|
|
17050
17108
|
});
|
|
17051
17109
|
registerAuditCommands(program);
|
|
@@ -17059,5 +17117,4 @@ const argv = process.argv.map((arg) => arg === "-v" ? "--version" : arg);
|
|
|
17059
17117
|
program.parseAsync(argv).catch((err) => {
|
|
17060
17118
|
fail(describeError(err));
|
|
17061
17119
|
});
|
|
17062
|
-
//#endregion
|
|
17063
17120
|
export { signatureKeyRef as A, generateDek as B, version as C, importSigningKey as D, generateSigningKeyMaterial as E, generateEncryptKeyMaterial as F, parseBranchTtl as H, kmsBlobKeyRef as I, kmsDecrypt as L, generatePostgresCredential as M, generateMysqlCredential as N, importVerifyingKey as O, generateDataKey as P, kmsEncrypt as R, writeProjectConfig as S, parseServiceToken as T, toBase64 as V, getDek as _, kmsResolveRecipient as a, tryBuildContext as b, fetchDecryptedSecrets as c, resolveApp as d, resolveAppEnv as f, resolveOrg as g, resolveGroup as h, kmsResolveKey as i, verifyMessage as j, signMessage as k, fetchDecryptedVersion as l, resolveEnvTarget as m, kmsCallerIdentity as n, syncGrantsForGroupLink as o, resolveBranch as p, kmsRecoverMaterial as r, encryptAndSetSecret as s, ensureM2mAdminToken as t, materializeEnv as u, isTokenAuth as v, createServiceToken as w, setFailThrows as x, principalPublicKeyJwk as y, wrapDek as z };
|
|
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
|
|
6
|
+
// src/mcp.ts
|
|
7
7
|
/** Lowercase-alphanumeric string for a fresh Postgres role name. */
|
|
8
8
|
function randomLower(length) {
|
|
9
9
|
const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -1023,5 +1023,4 @@ async function runMcpServer(options = {}) {
|
|
|
1023
1023
|
});
|
|
1024
1024
|
await server.connect(new StdioServerTransport());
|
|
1025
1025
|
}
|
|
1026
|
-
//#endregion
|
|
1027
1026
|
export { runMcpServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seekrit/cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
|
|
5
5
|
"homepage": "https://seekrit.dev/docs/guides/cli",
|
|
6
6
|
"keywords": [
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"node": ">=20"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@modelcontextprotocol/sdk": "^1.32.
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
42
42
|
"commander": "^15.0.0",
|
|
43
43
|
"zod": "^4.6.7"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^26.6.5",
|
|
47
|
-
"tsdown": "^0.23.
|
|
47
|
+
"tsdown": "^0.23.3",
|
|
48
48
|
"vitest": "^5.0.5",
|
|
49
49
|
"@seekrit/api-client": "0.0.1",
|
|
50
50
|
"@seekrit/core": "0.0.1",
|