@nuvlore/extension-bds 0.1.1 → 0.1.2
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/package.json +4 -16
- package/vendor/extension-read-only-toolkit/LICENSE +0 -202
- package/vendor/extension-read-only-toolkit/README.md +0 -29
- package/vendor/extension-read-only-toolkit/package.json +0 -46
- package/vendor/extension-read-only-toolkit/src/devops-data-paths.mjs +0 -222
- package/vendor/extension-read-only-toolkit/src/devops-diagnostics.mjs +0 -440
- package/vendor/extension-read-only-toolkit/src/enterprise-api-read.mjs +0 -180
- package/vendor/extension-read-only-toolkit/src/read-only-command-pack.mjs +0 -1
- package/vendor/extension-read-only-toolkit/src/read-only-plan.mjs +0 -45
- package/vendor/extension-read-only-toolkit/src/read-only-shell-policy.d.mts +0 -26
- package/vendor/extension-read-only-toolkit/src/read-only-shell-policy.mjs +0 -170
- package/vendor/extension-workday-infra-access/LICENSE +0 -202
- package/vendor/extension-workday-infra-access/README.md +0 -3
- package/vendor/extension-workday-infra-access/agents/workday-infra-access-readonly.yaml +0 -183
- package/vendor/extension-workday-infra-access/package.json +0 -49
- package/vendor/extension-workday-infra-access/skills/workday-infra-access/SKILL.md +0 -102
- package/vendor/extension-workday-infra-access/skills/workday-infra-access/references/access-boundary-runbook.md +0 -43
- package/vendor/extension-workday-infra-access/skills/workday-infra-access/references/access-runbook.md +0 -15
- package/vendor/extension-workday-infra-access/skills/workday-infra-access/references/access-scylla-cloud-runbook.md +0 -39
- package/vendor/extension-workday-infra-access/skills/workday-infra-access/references/access-spc-argo-runbook.md +0 -99
- package/vendor/extension-workday-infra-access/src/workday-access-profiles.mjs +0 -194
- package/vendor/extension-workday-infra-access/tools/aws_cust_federated_readonly_command_pack.mjs +0 -234
- package/vendor/extension-workday-infra-access/tools/gcp_readonly_command_pack.mjs +0 -167
- package/vendor/extension-workday-infra-access/tools/public_cloud_access_status.mjs +0 -48
- package/vendor/extension-workday-infra-access/tools/workday_boundary_login.mjs +0 -460
- package/vendor/extension-workday-infra-access/tools/workday_private_cloud_sudo_readonly_command.mjs +0 -386
- package/vendor/extension-workday-infra-access/tools/workday_public_cloud_environment_map.mjs +0 -189
- package/vendor/extension-workday-infra-access/tools/workday_public_cloud_kubernetes_read.mjs +0 -297
- package/vendor/extension-workday-infra-access/tools/workday_public_cloud_spc_login.mjs +0 -505
- package/vendor/extension-workday-infra-access/tools/workday_spc_argo_readonly_command_pack.mjs +0 -215
- package/vendor/extension-workday-infra-access/workflows/public-cloud-readonly-investigation.js +0 -99
package/vendor/extension-workday-infra-access/tools/workday_public_cloud_kubernetes_read.mjs
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
export const description = "Run one read-only kubectl operation through the local Workday public-cloud SPC kubeconfig/session. This tool never mutates Kubernetes resources.";
|
|
2
|
-
|
|
3
|
-
import { execFile } from "node:child_process";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import { defineReadOnlyTool, fail, ok } from "@nuvlore/extension-read-only-toolkit/devops-diagnostics";
|
|
8
|
-
import { shellQuote } from "../src/workday-access-profiles.mjs";
|
|
9
|
-
|
|
10
|
-
const execFileAsync = promisify(execFile);
|
|
11
|
-
|
|
12
|
-
const DEFAULT_KUBECTL_BINARY = "/usr/local/bin/kubectl";
|
|
13
|
-
const DEFAULT_KUBECONFIG_PATH = "$HOME/.kube/spc-config";
|
|
14
|
-
const DEFAULT_NAMESPACE = "bds";
|
|
15
|
-
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
16
|
-
const VALID_OPERATIONS = ["get", "describe", "auth-whoami", "auth-can-i"];
|
|
17
|
-
const VALID_OUTPUTS = ["json", "yaml", "wide", "name"];
|
|
18
|
-
const READONLY_RESOURCE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9./-]{0,252}$/;
|
|
19
|
-
const NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,252}$/;
|
|
20
|
-
const SELECTOR_PATTERN = /^[A-Za-z0-9_.:/-]+(=[A-Za-z0-9_.:/-]+)?(,[A-Za-z0-9_.:/-]+(=[A-Za-z0-9_.:/-]+)?)*$/;
|
|
21
|
-
const READONLY_RESOURCES = new Set([
|
|
22
|
-
"app",
|
|
23
|
-
"apps",
|
|
24
|
-
"application",
|
|
25
|
-
"applications",
|
|
26
|
-
"componentrelease",
|
|
27
|
-
"componentreleases",
|
|
28
|
-
"componentrelease.component.workday.com",
|
|
29
|
-
"componentreleases.component.workday.com",
|
|
30
|
-
"configmap",
|
|
31
|
-
"configmaps",
|
|
32
|
-
"crd",
|
|
33
|
-
"crds",
|
|
34
|
-
"customresourcedefinition",
|
|
35
|
-
"customresourcedefinitions",
|
|
36
|
-
"deploy",
|
|
37
|
-
"deployment",
|
|
38
|
-
"deployments",
|
|
39
|
-
"endpoint",
|
|
40
|
-
"endpoints",
|
|
41
|
-
"event",
|
|
42
|
-
"events",
|
|
43
|
-
"helmrelease",
|
|
44
|
-
"helmreleases",
|
|
45
|
-
"helmrelease.helm.toolkit.fluxcd.io",
|
|
46
|
-
"helmreleases.helm.toolkit.fluxcd.io",
|
|
47
|
-
"hpa",
|
|
48
|
-
"ingress",
|
|
49
|
-
"ingresses",
|
|
50
|
-
"pod",
|
|
51
|
-
"pods",
|
|
52
|
-
"scaledobject",
|
|
53
|
-
"scaledobjects",
|
|
54
|
-
"service",
|
|
55
|
-
"services",
|
|
56
|
-
"svc",
|
|
57
|
-
"statefulset",
|
|
58
|
-
"statefulsets",
|
|
59
|
-
"sts",
|
|
60
|
-
"vpa"
|
|
61
|
-
]);
|
|
62
|
-
|
|
63
|
-
function normalizeText(value) {
|
|
64
|
-
return typeof value === "string" ? value.trim() : "";
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function expandHome(value) {
|
|
68
|
-
const text = normalizeText(value);
|
|
69
|
-
if (text === "$HOME") return process.env.HOME || text;
|
|
70
|
-
if (text.startsWith("$HOME/")) return join(process.env.HOME || "$HOME", text.slice("$HOME/".length));
|
|
71
|
-
if (text === "~") return process.env.HOME || text;
|
|
72
|
-
if (text.startsWith("~/")) return join(process.env.HOME || "~", text.slice(2));
|
|
73
|
-
return text;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function validateReadonlyToken(value, pattern, label) {
|
|
77
|
-
const text = normalizeText(value);
|
|
78
|
-
if (!text) return "";
|
|
79
|
-
if (!pattern.test(text)) {
|
|
80
|
-
throw new Error(`${label} contains unsupported characters for a read-only kubectl execFile argument.`);
|
|
81
|
-
}
|
|
82
|
-
return text;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function validateResource(resource) {
|
|
86
|
-
const normalized = validateReadonlyToken(resource, READONLY_RESOURCE_PATTERN, "resource").toLowerCase();
|
|
87
|
-
if (!normalized) throw new Error("resource is required for kubectl get/describe/auth-can-i.");
|
|
88
|
-
if (!READONLY_RESOURCES.has(normalized)) {
|
|
89
|
-
throw new Error(`resource ${normalized} is not in the read-only Kubernetes resource allowlist.`);
|
|
90
|
-
}
|
|
91
|
-
return normalized;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function normalizeInput(input = {}) {
|
|
95
|
-
const operation = normalizeText(input.operation) || "get";
|
|
96
|
-
if (!VALID_OPERATIONS.includes(operation)) {
|
|
97
|
-
throw new Error(`operation must be one of ${VALID_OPERATIONS.join(", ")}.`);
|
|
98
|
-
}
|
|
99
|
-
const output = normalizeText(input.output) || "json";
|
|
100
|
-
if (!VALID_OUTPUTS.includes(output)) {
|
|
101
|
-
throw new Error(`output must be one of ${VALID_OUTPUTS.join(", ")}.`);
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
operation,
|
|
105
|
-
cluster: validateReadonlyToken(input.cluster, NAME_PATTERN, "cluster"),
|
|
106
|
-
context: validateReadonlyToken(input.context, NAME_PATTERN, "context"),
|
|
107
|
-
namespace: validateReadonlyToken(input.namespace, NAME_PATTERN, "namespace") || DEFAULT_NAMESPACE,
|
|
108
|
-
resource: input.resource ? validateResource(input.resource) : "",
|
|
109
|
-
name: validateReadonlyToken(input.name, NAME_PATTERN, "name"),
|
|
110
|
-
output,
|
|
111
|
-
labelSelector: validateReadonlyToken(input.labelSelector, SELECTOR_PATTERN, "labelSelector"),
|
|
112
|
-
fieldSelector: validateReadonlyToken(input.fieldSelector, SELECTOR_PATTERN, "fieldSelector"),
|
|
113
|
-
allNamespaces: input.allNamespaces === true,
|
|
114
|
-
kubeconfigPath: normalizeText(input.kubeconfigPath) || DEFAULT_KUBECONFIG_PATH,
|
|
115
|
-
kubectlBinary: normalizeText(input.kubectlBinary) || DEFAULT_KUBECTL_BINARY,
|
|
116
|
-
timeoutMs: Number.isInteger(input.timeoutMs) ? Math.max(1000, input.timeoutMs) : DEFAULT_TIMEOUT_MS
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function kubectlEnv() {
|
|
121
|
-
return {
|
|
122
|
-
...process.env,
|
|
123
|
-
PATH: [
|
|
124
|
-
"/Users/boqiang.liang/bin",
|
|
125
|
-
"/usr/local/bin",
|
|
126
|
-
"/opt/homebrew/bin",
|
|
127
|
-
process.env.PATH || ""
|
|
128
|
-
].filter(Boolean).join(":")
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function baseKubectlArgs(input) {
|
|
133
|
-
const args = ["--kubeconfig", expandHome(input.kubeconfigPath)];
|
|
134
|
-
const context = input.context || input.cluster;
|
|
135
|
-
if (context) args.push("--context", context);
|
|
136
|
-
return args;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function kubectlArgs(input) {
|
|
140
|
-
const args = baseKubectlArgs(input);
|
|
141
|
-
if (input.operation === "auth-whoami") return [...args, "auth", "whoami"];
|
|
142
|
-
if (input.operation === "auth-can-i") {
|
|
143
|
-
return [
|
|
144
|
-
...args,
|
|
145
|
-
"auth",
|
|
146
|
-
"can-i",
|
|
147
|
-
"get",
|
|
148
|
-
input.resource,
|
|
149
|
-
"-n",
|
|
150
|
-
input.namespace
|
|
151
|
-
];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!input.resource) throw new Error("resource is required for kubectl get/describe.");
|
|
155
|
-
args.push(input.operation, input.resource);
|
|
156
|
-
if (input.name) args.push(input.name);
|
|
157
|
-
if (input.allNamespaces) {
|
|
158
|
-
args.push("-A");
|
|
159
|
-
} else {
|
|
160
|
-
args.push("-n", input.namespace);
|
|
161
|
-
}
|
|
162
|
-
if (input.operation === "get") {
|
|
163
|
-
args.push("-o", input.output);
|
|
164
|
-
if (input.labelSelector) args.push("-l", input.labelSelector);
|
|
165
|
-
if (input.fieldSelector) args.push("--field-selector", input.fieldSelector);
|
|
166
|
-
}
|
|
167
|
-
return args;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function commandString(command, args) {
|
|
171
|
-
return [command, ...args].map(shellQuote).join(" ");
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function parseJsonIfRequested(input, stdout) {
|
|
175
|
-
if (input.operation !== "get" || input.output !== "json" || !normalizeText(stdout)) return undefined;
|
|
176
|
-
try {
|
|
177
|
-
return JSON.parse(stdout);
|
|
178
|
-
} catch (_error) {
|
|
179
|
-
return undefined;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async function publicCloudKubernetesRead(input, options = {}) {
|
|
184
|
-
const normalized = normalizeInput(input);
|
|
185
|
-
const args = kubectlArgs(normalized);
|
|
186
|
-
const execFileImpl = options.execFileImpl || execFileAsync;
|
|
187
|
-
const command = commandString(normalized.kubectlBinary, args);
|
|
188
|
-
try {
|
|
189
|
-
const result = await execFileImpl(normalized.kubectlBinary, args, {
|
|
190
|
-
env: kubectlEnv(),
|
|
191
|
-
maxBuffer: 20 * 1024 * 1024,
|
|
192
|
-
timeout: normalized.timeoutMs
|
|
193
|
-
});
|
|
194
|
-
const stdout = normalizeText(result.stdout);
|
|
195
|
-
return JSON.stringify({
|
|
196
|
-
mode: "public-cloud-kubernetes-read",
|
|
197
|
-
ok: true,
|
|
198
|
-
target: {
|
|
199
|
-
cluster: normalized.cluster,
|
|
200
|
-
context: normalized.context || normalized.cluster || "",
|
|
201
|
-
namespace: normalized.allNamespaces ? "" : normalized.namespace,
|
|
202
|
-
allNamespaces: normalized.allNamespaces,
|
|
203
|
-
operation: normalized.operation,
|
|
204
|
-
resource: normalized.resource,
|
|
205
|
-
name: normalized.name,
|
|
206
|
-
output: normalized.operation === "get" ? normalized.output : ""
|
|
207
|
-
},
|
|
208
|
-
command,
|
|
209
|
-
stdout,
|
|
210
|
-
stderr: normalizeText(result.stderr),
|
|
211
|
-
json: parseJsonIfRequested(normalized, stdout),
|
|
212
|
-
remoteWrite: "not_executed"
|
|
213
|
-
}, null, 2);
|
|
214
|
-
} catch (error) {
|
|
215
|
-
return JSON.stringify({
|
|
216
|
-
mode: "public-cloud-kubernetes-read",
|
|
217
|
-
ok: false,
|
|
218
|
-
target: {
|
|
219
|
-
cluster: normalized.cluster,
|
|
220
|
-
context: normalized.context || normalized.cluster || "",
|
|
221
|
-
namespace: normalized.allNamespaces ? "" : normalized.namespace,
|
|
222
|
-
allNamespaces: normalized.allNamespaces,
|
|
223
|
-
operation: normalized.operation,
|
|
224
|
-
resource: normalized.resource,
|
|
225
|
-
name: normalized.name,
|
|
226
|
-
output: normalized.operation === "get" ? normalized.output : ""
|
|
227
|
-
},
|
|
228
|
-
command,
|
|
229
|
-
stdout: normalizeText(error?.stdout),
|
|
230
|
-
stderr: normalizeText(error?.stderr || error?.message),
|
|
231
|
-
remoteWrite: "not_executed"
|
|
232
|
-
}, null, 2);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export class WorkdayPublicCloudKubernetesReadService {
|
|
237
|
-
constructor(options = {}) {
|
|
238
|
-
this.options = options;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async invoke(input) {
|
|
242
|
-
return publicCloudKubernetesRead(input, this.options);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const defaultService = new WorkdayPublicCloudKubernetesReadService();
|
|
247
|
-
|
|
248
|
-
export const __test = {
|
|
249
|
-
WorkdayPublicCloudKubernetesReadService,
|
|
250
|
-
baseKubectlArgs,
|
|
251
|
-
commandString,
|
|
252
|
-
expandHome,
|
|
253
|
-
kubectlArgs,
|
|
254
|
-
normalizeInput,
|
|
255
|
-
publicCloudKubernetesRead,
|
|
256
|
-
validateResource
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const sharedTool = defineReadOnlyTool({
|
|
260
|
-
name: "workday_public_cloud_kubernetes_read",
|
|
261
|
-
description: "Run one read-only kubectl operation through the local Workday public-cloud SPC kubeconfig/session. This tool never mutates Kubernetes resources.",
|
|
262
|
-
meta: {
|
|
263
|
-
permissions: { rule: "ask" }
|
|
264
|
-
},
|
|
265
|
-
inputSchema: {
|
|
266
|
-
operation: z.enum(VALID_OPERATIONS).default("get"),
|
|
267
|
-
cluster: z.string().trim().min(1).max(253).optional(),
|
|
268
|
-
context: z.string().trim().min(1).max(253).optional(),
|
|
269
|
-
namespace: z.string().trim().min(1).max(253).default(DEFAULT_NAMESPACE),
|
|
270
|
-
resource: z.string().trim().min(1).max(253).optional(),
|
|
271
|
-
name: z.string().trim().min(1).max(253).optional(),
|
|
272
|
-
output: z.enum(VALID_OUTPUTS).default("json"),
|
|
273
|
-
labelSelector: z.string().trim().min(1).max(1024).optional(),
|
|
274
|
-
fieldSelector: z.string().trim().min(1).max(1024).optional(),
|
|
275
|
-
allNamespaces: z.boolean().default(false),
|
|
276
|
-
kubeconfigPath: z.string().trim().min(1).max(2048).default(DEFAULT_KUBECONFIG_PATH),
|
|
277
|
-
kubectlBinary: z.string().trim().min(1).max(2048).default(DEFAULT_KUBECTL_BINARY),
|
|
278
|
-
timeoutMs: z.number().int().positive().max(120_000).default(DEFAULT_TIMEOUT_MS)
|
|
279
|
-
},
|
|
280
|
-
async handler(input) {
|
|
281
|
-
try {
|
|
282
|
-
return ok(await defaultService.invoke(input));
|
|
283
|
-
} catch (error) {
|
|
284
|
-
return fail(error);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
export default {
|
|
290
|
-
name: sharedTool.name,
|
|
291
|
-
description,
|
|
292
|
-
inputSchema: sharedTool.inputSchema,
|
|
293
|
-
meta: sharedTool.meta,
|
|
294
|
-
annotations: sharedTool.annotations,
|
|
295
|
-
invoke: sharedTool.invoke,
|
|
296
|
-
handler: sharedTool.handler
|
|
297
|
-
};
|