@nuvlore/extension-access-okta-slack 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.
Files changed (75) hide show
  1. package/package.json +4 -19
  2. package/vendor/extension-sso/LICENSE +0 -202
  3. package/vendor/extension-sso/README.md +0 -35
  4. package/vendor/extension-sso/commands/okta-login.md +0 -10
  5. package/vendor/extension-sso/package.json +0 -53
  6. package/vendor/extension-sso/skills/workday-okta-auth/SKILL.md +0 -20
  7. package/vendor/extension-sso/src/autoAuth.mjs +0 -25
  8. package/vendor/extension-sso/src/browserLogin.mjs +0 -1
  9. package/vendor/extension-sso/src/chromeCookies.mjs +0 -1
  10. package/vendor/extension-sso/src/cookieJar.mjs +0 -1
  11. package/vendor/extension-sso/src/extensionAccess.mjs +0 -173
  12. package/vendor/extension-sso/src/htmlSessionExtract.mjs +0 -1
  13. package/vendor/extension-sso/src/oktaAppAccess.mjs +0 -133
  14. package/vendor/extension-sso/src/oktaAppCatalog.mjs +0 -118
  15. package/vendor/extension-sso/src/oktaAppRead.mjs +0 -119
  16. package/vendor/extension-sso/src/oktaConfig.mjs +0 -12
  17. package/vendor/extension-sso/src/oktaSession.mjs +0 -32
  18. package/vendor/extension-sso/src/serviceOperations.mjs +0 -407
  19. package/vendor/extension-sso/src/serviceReadSearch.mjs +0 -395
  20. package/vendor/extension-sso/src/serviceReads.mjs +0 -109
  21. package/vendor/extension-sso/src/serviceRegistry.mjs +0 -497
  22. package/vendor/extension-sso/src/sessionAuth.mjs +0 -339
  23. package/vendor/extension-sso/src/sessionManager.mjs +0 -212
  24. package/vendor/extension-sso/src/sessionValidate.mjs +0 -74
  25. package/vendor/extension-sso/src/ssoConfig.mjs +0 -31
  26. package/vendor/extension-sso/src/ssoHttpRead.mjs +0 -292
  27. package/vendor/extension-sso/tools/extension-access-tools.shared.mjs +0 -16
  28. package/vendor/extension-sso/tools/okta-app-tools.shared.mjs +0 -5
  29. package/vendor/extension-sso/tools/okta-tools.shared.mjs +0 -18
  30. package/vendor/extension-sso/tools/workday_extension_read.mjs +0 -51
  31. package/vendor/extension-sso/tools/workday_extension_read_access.mjs +0 -65
  32. package/vendor/extension-sso/tools/workday_list_extension_access.mjs +0 -39
  33. package/vendor/extension-sso/tools/workday_list_okta_apps.mjs +0 -18
  34. package/vendor/extension-sso/tools/workday_list_service_operations.mjs +0 -41
  35. package/vendor/extension-sso/tools/workday_list_service_read_search_capabilities.mjs +0 -34
  36. package/vendor/extension-sso/tools/workday_list_services.mjs +0 -31
  37. package/vendor/extension-sso/tools/workday_okta_app_probe_all.mjs +0 -24
  38. package/vendor/extension-sso/tools/workday_okta_app_read.mjs +0 -30
  39. package/vendor/extension-sso/tools/workday_okta_app_read_access.mjs +0 -25
  40. package/vendor/extension-sso/tools/workday_okta_auth_header.mjs +0 -58
  41. package/vendor/extension-sso/tools/workday_okta_login.mjs +0 -65
  42. package/vendor/extension-sso/tools/workday_okta_open_app.mjs +0 -63
  43. package/vendor/extension-sso/tools/workday_okta_open_service.mjs +0 -63
  44. package/vendor/extension-sso/tools/workday_okta_status.mjs +0 -35
  45. package/vendor/extension-sso/tools/workday_service_auth.mjs +0 -65
  46. package/vendor/extension-sso/tools/workday_service_operation_read.mjs +0 -51
  47. package/vendor/extension-sso/tools/workday_service_read.mjs +0 -56
  48. package/vendor/extension-sso/tools/workday_service_search.mjs +0 -62
  49. package/vendor/extension-sso/tools/workday_sso_cookie_header.mjs +0 -59
  50. package/vendor/extension-sso/vendor/extension-browser-sso/LICENSE +0 -202
  51. package/vendor/extension-sso/vendor/extension-browser-sso/README.md +0 -16
  52. package/vendor/extension-sso/vendor/extension-browser-sso/package.json +0 -47
  53. package/vendor/extension-sso/vendor/extension-browser-sso/src/autoAuth.mjs +0 -47
  54. package/vendor/extension-sso/vendor/extension-browser-sso/src/browserHttpRead.mjs +0 -76
  55. package/vendor/extension-sso/vendor/extension-browser-sso/src/browserLogin.mjs +0 -24
  56. package/vendor/extension-sso/vendor/extension-browser-sso/src/chromeCookies.mjs +0 -192
  57. package/vendor/extension-sso/vendor/extension-browser-sso/src/cookieJar.mjs +0 -132
  58. package/vendor/extension-sso/vendor/extension-browser-sso/src/extensions/toolkit/devops-data-paths.mjs +0 -222
  59. package/vendor/extension-sso/vendor/extension-browser-sso/src/extensions/toolkit/devops-diagnostics.mjs +0 -440
  60. package/vendor/extension-sso/vendor/extension-browser-sso/src/extensions/toolkit/enterprise-api-read.mjs +0 -180
  61. package/vendor/extension-sso/vendor/extension-browser-sso/src/extensions/toolkit/read-only-command-pack.mjs +0 -1
  62. package/vendor/extension-sso/vendor/extension-browser-sso/src/extensions/toolkit/read-only-plan.mjs +0 -45
  63. package/vendor/extension-sso/vendor/extension-browser-sso/src/extensions/toolkit/read-only-shell-policy.d.mts +0 -26
  64. package/vendor/extension-sso/vendor/extension-browser-sso/src/extensions/toolkit/read-only-shell-policy.mjs +0 -170
  65. package/vendor/extension-sso/vendor/extension-browser-sso/src/htmlSessionExtract.mjs +0 -64
  66. package/vendor/extension-sso/vendor/extension-read-only-toolkit/LICENSE +0 -202
  67. package/vendor/extension-sso/vendor/extension-read-only-toolkit/README.md +0 -29
  68. package/vendor/extension-sso/vendor/extension-read-only-toolkit/package.json +0 -43
  69. package/vendor/extension-sso/vendor/extension-read-only-toolkit/src/devops-data-paths.mjs +0 -222
  70. package/vendor/extension-sso/vendor/extension-read-only-toolkit/src/devops-diagnostics.mjs +0 -440
  71. package/vendor/extension-sso/vendor/extension-read-only-toolkit/src/enterprise-api-read.mjs +0 -180
  72. package/vendor/extension-sso/vendor/extension-read-only-toolkit/src/read-only-command-pack.mjs +0 -1
  73. package/vendor/extension-sso/vendor/extension-read-only-toolkit/src/read-only-plan.mjs +0 -45
  74. package/vendor/extension-sso/vendor/extension-read-only-toolkit/src/read-only-shell-policy.d.mts +0 -26
  75. package/vendor/extension-sso/vendor/extension-read-only-toolkit/src/read-only-shell-policy.mjs +0 -170
@@ -1,180 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { promisify } from "node:util";
3
- import { assertNonHtmlApiResponse } from "./devops-diagnostics.mjs";
4
-
5
- const execFileAsync = promisify(execFile);
6
-
7
- export function normalizeBaseUrl(value) {
8
- const raw = String(value || "").trim();
9
- if (!raw) return "";
10
- try {
11
- const url = new URL(raw);
12
- url.pathname = url.pathname.replace(/\/+$/, "");
13
- url.search = "";
14
- url.hash = "";
15
- return url.toString().replace(/\/+$/, "");
16
- } catch {
17
- return "";
18
- }
19
- }
20
-
21
- export function appendQueryValue(searchParams, key, value) {
22
- if (Array.isArray(value)) {
23
- for (const item of value) appendQueryValue(searchParams, key, item);
24
- return;
25
- }
26
- if (value === undefined || value === null) return;
27
- searchParams.append(key, String(value));
28
- }
29
-
30
- export function buildRequestUrl(baseUrl, requestPath, query) {
31
- const url = new URL(`${baseUrl}${requestPath}`);
32
- for (const [key, value] of Object.entries(query || {})) {
33
- if (key) appendQueryValue(url.searchParams, key, value);
34
- }
35
- return url;
36
- }
37
-
38
- export function extractTokenFromKeychainSecret(rawSecret = "") {
39
- const text = typeof rawSecret === "string" ? rawSecret.trim() : "";
40
- if (!text) return "";
41
- try {
42
- const parsed = JSON.parse(text);
43
- return typeof parsed?.token === "string" ? parsed.token.trim() : "";
44
- } catch {
45
- return text;
46
- }
47
- }
48
-
49
- export async function readTokenFromKeychain({ execFileImpl = execFileAsync, keychainService, keychainAccount }) {
50
- try {
51
- const result = await execFileImpl("/usr/bin/security", [
52
- "find-generic-password",
53
- "-s",
54
- keychainService,
55
- "-a",
56
- keychainAccount,
57
- "-w"
58
- ], { maxBuffer: 1024 * 1024 });
59
- return extractTokenFromKeychainSecret(String(result?.stdout || ""));
60
- } catch {
61
- return "";
62
- }
63
- }
64
-
65
- export function parseResponseBody(text) {
66
- try {
67
- return JSON.parse(text);
68
- } catch {
69
- return String(text || "");
70
- }
71
- }
72
-
73
- export class KeychainBearerAuthProvider {
74
- constructor(options = {}) {
75
- this.mode = "keychain";
76
- this.execFileImpl = options.execFileImpl || execFileAsync;
77
- this.keychainService = options.keychainService;
78
- this.keychainAccount = options.keychainAccount;
79
- this.authorizationHeader = options.authorizationHeader || ((token) => `Bearer ${token}`);
80
- }
81
-
82
- async buildHeaders() {
83
- const token = await readTokenFromKeychain({
84
- execFileImpl: this.execFileImpl,
85
- keychainService: this.keychainService,
86
- keychainAccount: this.keychainAccount
87
- });
88
- if (!token) {
89
- throw new Error(
90
- `Keychain token missing for ${this.keychainService}/${this.keychainAccount}. Save the personal access token in Keychain.`
91
- );
92
- }
93
- return { authorization: this.authorizationHeader(token) };
94
- }
95
- }
96
-
97
- /** Cookie SSO auth — inject `resolveCookieHeader(input)` from @nuvlore/extension-browser-sso. */
98
- export class CookieSsoAuthProvider {
99
- constructor(options = {}) {
100
- this.mode = "cookie-sso";
101
- this.resolveCookieHeader = options.resolveCookieHeader;
102
- if (typeof this.resolveCookieHeader !== "function") {
103
- throw new Error("CookieSsoAuthProvider requires resolveCookieHeader(input) async function.");
104
- }
105
- }
106
-
107
- async buildHeaders(input = {}) {
108
- const cookie = await this.resolveCookieHeader(input);
109
- if (!cookie) {
110
- throw new Error("Browser SSO cookie header is missing. Open the service in Chrome via Okta and retry.");
111
- }
112
- return { cookie };
113
- }
114
- }
115
-
116
- export class EnterpriseApiReadService {
117
- constructor(options = {}) {
118
- this.fetchImpl = options.fetchImpl || globalThis.fetch;
119
- this.execFileImpl = options.execFileImpl || execFileAsync;
120
- this.serviceName = options.serviceName;
121
- this.defaultBaseUrl = options.defaultBaseUrl;
122
- this.defaultUserAgent = options.defaultUserAgent;
123
- this.keychainService = options.keychainService;
124
- this.keychainAccount = options.keychainAccount;
125
- this.normalizePath = options.normalizePath;
126
- this.accept = options.accept || "application/json";
127
- this.extraHeaders = options.extraHeaders || {};
128
- this.authorizationHeader = options.authorizationHeader || ((token) => `Bearer ${token}`);
129
- this.formatErrorPayload = options.formatErrorPayload;
130
- this.includeMethodInPayload = options.includeMethodInPayload === true;
131
- this.transformPayload = options.transformPayload || ((payload) => payload);
132
- this.authProvider =
133
- options.authProvider ??
134
- new KeychainBearerAuthProvider({
135
- execFileImpl: this.execFileImpl,
136
- keychainService: this.keychainService,
137
- keychainAccount: this.keychainAccount,
138
- authorizationHeader: this.authorizationHeader
139
- });
140
- }
141
-
142
- async invoke(input = {}) {
143
- if (typeof this.fetchImpl !== "function") {
144
- throw new Error(`Fetch API is unavailable for ${this.serviceName} requests.`);
145
- }
146
- const baseUrl = normalizeBaseUrl(input.baseUrl || this.defaultBaseUrl);
147
- if (!baseUrl) {
148
- throw new Error(`${this.serviceName} base URL is missing or invalid. Expected something like ${this.defaultBaseUrl}.`);
149
- }
150
- const requestPath = this.normalizePath(input.restPath || input.path);
151
- const url = buildRequestUrl(baseUrl, requestPath, input.query);
152
- const authHeaders = await this.authProvider.buildHeaders(input);
153
- const response = await this.fetchImpl(url, {
154
- method: "GET",
155
- headers: {
156
- accept: this.accept,
157
- "user-agent": this.defaultUserAgent,
158
- ...this.extraHeaders,
159
- ...authHeaders,
160
- ...(input.headers ?? {})
161
- }
162
- });
163
- const text = await response.text();
164
- assertNonHtmlApiResponse(this.serviceName, text);
165
- const payload = parseResponseBody(text);
166
- if (!response.ok) {
167
- throw new Error(`${this.serviceName} request failed with status ${response.status}: ${this.formatErrorPayload(payload)}.`);
168
- }
169
- const output = {
170
- baseUrl,
171
- restPath: requestPath,
172
- path: requestPath,
173
- url: url.toString(),
174
- status: response.status,
175
- data: this.transformPayload(payload)
176
- };
177
- if (this.includeMethodInPayload) output.method = "GET";
178
- return JSON.stringify(output, null, 2);
179
- }
180
- }
@@ -1 +0,0 @@
1
- export * from "./read-only-plan.mjs";
@@ -1,45 +0,0 @@
1
- export function shellQuote(value) {
2
- return `'${String(value).replaceAll("'", "'\\''")}'`;
3
- }
4
-
5
- export function remoteCommandInput({ host, command, timeoutMs = 60_000 }) {
6
- return {
7
- targetType: "ssh",
8
- host,
9
- command,
10
- timeoutMs
11
- };
12
- }
13
-
14
- export class ReadOnlyStep {
15
- constructor({ id, purpose, command, expectedEvidence }) {
16
- this.id = id;
17
- this.purpose = purpose;
18
- this.command = command;
19
- this.expectedEvidence = expectedEvidence;
20
- }
21
-
22
- toMarkdown() {
23
- return [
24
- `### ${this.id}`,
25
- `Purpose: ${this.purpose}`,
26
- "",
27
- "```bash",
28
- this.command,
29
- "```",
30
- "",
31
- `Evidence to capture: ${this.expectedEvidence}`
32
- ].join("\n");
33
- }
34
-
35
- toJson() {
36
- return {
37
- id: this.id,
38
- purpose: this.purpose,
39
- command: this.command,
40
- expectedEvidence: this.expectedEvidence
41
- };
42
- }
43
- }
44
-
45
- export class ReadOnlyCommand extends ReadOnlyStep {}
@@ -1,26 +0,0 @@
1
- export type ReadOnlyValidation = {
2
- allowed: boolean;
3
- reason: string;
4
- };
5
-
6
- export type ReadOnlyPolicyConfig = {
7
- extraReadOnlyCommandPatterns?: RegExp[];
8
- };
9
-
10
- export const READ_ONLY_ALLOWED_TOOLS: readonly string[];
11
- export const READ_ONLY_DISALLOWED_TOOLS: readonly string[];
12
- export const MUTATING_SHELL_TOKENS: RegExp[];
13
- export const READ_ONLY_COMMAND_PATTERNS: RegExp[];
14
-
15
- export class ReadOnlyShellPolicy {
16
- constructor(config?: ReadOnlyPolicyConfig);
17
- validate(command: string): ReadOnlyValidation;
18
- readOnlyPatterns(): RegExp[];
19
- }
20
-
21
- export class ReadOnlyPolicyDescription {
22
- text(): string;
23
- }
24
-
25
- export function isReadOnlyShellCommand(command: string, config?: ReadOnlyPolicyConfig): ReadOnlyValidation;
26
- export function describeReadOnlyPolicy(): string;
@@ -1,170 +0,0 @@
1
- export const READ_ONLY_ALLOWED_TOOLS = [
2
- "Read",
3
- "Glob",
4
- "Grep"
5
- ];
6
-
7
- export const READ_ONLY_DISALLOWED_TOOLS = [
8
- "Agent",
9
- "Task",
10
- "TaskCreate",
11
- "Write",
12
- "Edit",
13
- "NotebookEdit",
14
- "TodoWrite",
15
- "ExitPlanMode",
16
- "EnterWorktree",
17
- "WebFetch",
18
- "WebSearch",
19
- "AskUserQuestion"
20
- ];
21
-
22
- export const MUTATING_SHELL_TOKENS = [
23
- /\bapply\b/i,
24
- /\bcreate\b/i,
25
- /\bdelete\b/i,
26
- /\bdestroy\b/i,
27
- /\bedit\b/i,
28
- /\binstall\b/i,
29
- /\bpatch\b/i,
30
- /\bpush\b/i,
31
- /\breplace\b/i,
32
- /\brestart\b/i,
33
- /\brollout\s+(restart|undo)\b/i,
34
- /\brm\b/i,
35
- /\bscale\b/i,
36
- /\bset\b/i,
37
- /\bstart\b/i,
38
- /\bstop\b/i,
39
- /\btaint\b/i,
40
- /\buntaint\b/i,
41
- /\bupgrade\b/i,
42
- /\bwrite\b/i,
43
- /\bchmod\b/i,
44
- /\bchown\b/i,
45
- /\bmv\b/i,
46
- /\bcp\b/i,
47
- /\bmkdir\b/i,
48
- /\btouch\b/i,
49
- /\btee\b/i,
50
- /\bcurl\b.*\|\s*(sh|bash)\b/i,
51
- /\bwget\b.*\|\s*(sh|bash)\b/i,
52
- /(^|[^<])>(?!\s*&\d)/,
53
- />>/,
54
- /\|\s*(xargs\s+)?(sh|bash|kubectl\s+apply|terraform\s+apply|helm\s+upgrade|rm|tee)\b/i,
55
- /(^|[^&|]);\s*/,
56
- /&&|\|\|/,
57
- /`|\$\(/,
58
- /\b--write\b/i,
59
- /\b--force\b/i,
60
- /\b--yes\b|\b-y\b/i
61
- ];
62
-
63
- export const READ_ONLY_COMMAND_PATTERNS = [
64
- /^\s*pwd\s*$/,
65
- /^\s*ls(\s|$)/,
66
- /^\s*find\s+/,
67
- /^\s*(rg|grep)\s+/,
68
- /^\s*(cat|head|tail|sed|awk|wc)\s+/,
69
- /^\s*curl\s+-k\s+-sS\s+-o\s+\/dev\/null\s+-w\s+'[A-Za-z0-9_=%{}:./ -]+\\n'\s+https:\/\/[A-Za-z0-9_.:-]+\/[A-Za-z0-9_./:@%+=,~-]+(\s|$)/,
70
- /^\s*hostname(\s|$)/,
71
- /^\s*command\s+-v\s+/,
72
- /^\s*klist(\s|$)/,
73
- /^\s*git\s+(status|diff|log|show|branch|remote|rev-parse|describe|ls-files)(\s|$)/,
74
- /^\s*kubectl\s+(get|describe|logs|top|api-resources|api-versions|explain|version|cluster-info|config\s+(current-context|get-contexts|view))(\s|$)/,
75
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+airflow\s+dags\s+list-runs\s+-d\s+[A-Za-z0-9_.:@/-]+(?:\s+--no-backfill)?(?:\s+--output\s+(?:json|table|yaml|plain))?(?:\s+\|\s+head\s+-\d+)?\s*$/,
76
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+airflow\s+tasks\s+states-for-dag-run\s+[A-Za-z0-9_.:@/-]+\s+[A-Za-z0-9_.:@+=,~%/-]+(?:\s+--output\s+(?:json|table|yaml|plain))?(?:\s+\|\s+head\s+-\d+)?\s*$/,
77
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+airflow\s+tasks\s+logs\s+[A-Za-z0-9_.:@/-]+\s+[A-Za-z0-9_.:@/-]+\s+[A-Za-z0-9_.:@+=,~%/-]+\s+--try-number\s+\d+\s+\|\s+head\s+-\d+\s*$/,
78
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+airflow\s+tasks\s+render\s+[A-Za-z0-9_.:@/-]+\s+[A-Za-z0-9_.:@/-]+\s+[A-Za-z0-9_.:@+=,~%/-]+\s+\|\s+head\s+-\d+\s*$/,
79
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+cat\s+\/(?:(?:data|hadoop\/disk2)\/workday-bds-airflow|data\/airflow)\/logs\/[A-Za-z0-9_.:@+=,~%/-]+(?:\s+\|\s+head\s+-\d+)?\s*$/,
80
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+grep\s+-E\s+'[A-Za-z0-9_.:@+=,~%|/-]+'\s+\/(?:(?:data|hadoop\/disk2)\/workday-bds-airflow|data\/airflow)\/logs\/[A-Za-z0-9_.:@+=,~%/-]+(?:\s+\|\s+tail\s+-n\s+\d+)?\s*$/,
81
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+grep\s+-E\s+'[A-Za-z0-9_.:@+=,~%|/-]+'\s+\/usr\/local\/airflow\/(?:airflow_s3_config\.json|dags\/[A-Za-z0-9_.:@+=,~%/-]+)(?:\s+\|\s+(?:head|tail)\s+-\d+)?\s*$/,
82
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+printenv\s+\|\s+grep\s+-E\s+'\^\(AWS_\|GOOGLE_\|GOOGLE_APPLICATION_CREDENTIALS\|HADOOP_\|HADOOP_CONF_DIR\|AIRFLOW_S3_CONFIG_PATH\)'\s+\|\s+sed\s+-E\s+'s\/=\.\*\/=\[present\]\/'\s+\|\s+head\s+-\d+\s*$/,
83
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+printenv\s+HADOOP_CREDENTIAL_PROVIDER_PATH\s*$/,
84
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+hadoop\s+credential\s+list\s+\|\s+head\s+-\d+\s*$/,
85
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+grep\s+-h\s+-E\s+'fs\.s3a\.aws\.credentials\.provider\|fs\.s3a\.endpoint\|fs\.s3a\.impl\|google\.cloud\.auth'\s+\/etc\/hadoop\/conf\/(?:core-site|hdfs-site|mapred-site|yarn-site)\.xml(?:\s+\/etc\/hadoop\/conf\/(?:core-site|hdfs-site|mapred-site|yarn-site)\.xml)*\s+\|\s+head\s+-\d+\s*$/,
86
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+cat\s+\/usr\/local\/airflow\/(?:airflow_s3_config\.json|dags\/[A-Za-z0-9_.:@+=,~%/-]+)(?:\s+\|\s+head\s+-\d+)?\s*$/,
87
- /^\s*kubectl\s+exec\s+-n\s+airflow\s+(?:pod\/)?[A-Za-z0-9._:@%/-]+(?:\s+-c\s+[A-Za-z0-9._:@%/-]+)?\s+--\s+hadoop\s+fs\s+-(?:ls|du|count|stat|test)(?:\s+-d)?\s+s3a:\/\/[A-Za-z0-9][A-Za-z0-9_.-]*[A-Za-z0-9](?:\/[A-Za-z0-9_.:@+=,~%/-]*)?(?:\s+\|\s+head\s+-\d+)?\s*$/,
88
- /^\s*kcsb\s+(get|describe|logs|top|api-resources|api-versions|explain|version|cluster-info|config\s+(current-context|get-contexts|view))(\s|$)/,
89
- /^\s*kinit\s+-kt\s+\/[A-Za-z0-9._/-]+\s+[A-Za-z0-9_.@/-]+(\s|$)/,
90
- /^\s*yarn\s+application\s+-status\s+application_[0-9]+_[0-9]+(\s|$)/,
91
- /^\s*yarn\s+logs\s+-applicationId\s+application_[0-9]+_[0-9]+(\s|$)/,
92
- /^\s*yarn\s+node\s+-status\s+(?!-)[A-Za-z0-9._:@%-]+:45454(\s|$)/,
93
- /^\s*hdfs\s+crypto\s+-listZones(\s|$)/,
94
- /^\s*hdfs\s+dfs\s+-(ls|du|find|cat|tail|test|count|stat)\s+/,
95
- /^\s*hdfs\s+dfsadmin\s+-report(\s|$)/,
96
- /^\s*hadoop\s+fs\s+-(ls|cat|head|du|count|stat|test|text)(\s|$)/,
97
- /^\s*helm\s+(list|status|history|get|version|repo\s+list)(\s|$)/,
98
- /^\s*aws\s+(?:--profile\s+\S+\s+)?(?:--region\s+\S+\s+)?sts\s+get-caller-identity(\s|$)/,
99
- /^\s*aws\s+(?:--profile\s+\S+\s+)?(?:--region\s+\S+\s+)?[a-z0-9-]+\s+(describe|list|get|head)-[a-z0-9-]+(\s|$)/,
100
- /^\s*aws\s+(?:--profile\s+\S+\s+)?(?:--region\s+\S+\s+)?s3\s+ls(\s|$)/,
101
- /^\s*gcloud\s+(?:--project=\S+\s+)?(?:--account=\S+\s+)?auth\s+list(\s|$)/,
102
- /^\s*gcloud\s+(?:--project=\S+\s+)?(?:--account=\S+\s+)?config\s+list(\s|$)/,
103
- /^\s*gcloud\s+(?:--project=\S+\s+)?(?:--account=\S+\s+)?projects\s+(describe|get-iam-policy)(\s|$)/,
104
- /^\s*gcloud\s+(?:--project=\S+\s+)?(?:--account=\S+\s+)?[a-z0-9-]+(?:\s+[a-z0-9-]+)*\s+(list|describe|read)(\s|$)/,
105
- /^\s*gcloud\s+storage\s+(buckets|objects)\s+list(\s|$)/,
106
- /^\s*terraform\s+(version|show|state\s+list|state\s+show|providers|workspace\s+show|output)(\s|$)/,
107
- /^\s*docker\s+(ps|images|logs|inspect|stats|version|info)(\s|$)/,
108
- /^\s*(df|du|free|uptime|uname|whoami|id|date|env|printenv)(\s|$)/,
109
- /^\s*journalctl\s+-k\s+--since\s+/,
110
- /^\s*(systemctl|journalctl)\s+(status|-u|--unit)(\s|$)/
111
- ];
112
-
113
- export class ReadOnlyShellPolicy {
114
- constructor(config = {}) {
115
- this.config = config;
116
- }
117
-
118
- validate(command) {
119
- const normalized = String(command ?? "").trim();
120
-
121
- if (!normalized) {
122
- return { allowed: false, reason: "empty shell command" };
123
- }
124
- if (normalized.includes("\n") || normalized.includes("\r")) {
125
- return { allowed: false, reason: "multi-line shell commands are not allowed" };
126
- }
127
-
128
- const mutatingToken = MUTATING_SHELL_TOKENS.find((token) => token.test(normalized));
129
- if (mutatingToken) {
130
- return {
131
- allowed: false,
132
- reason: `shell command contains mutating or high-risk syntax: ${mutatingToken.source}`
133
- };
134
- }
135
-
136
- if (this.readOnlyPatterns().some((pattern) => pattern.test(normalized))) {
137
- return { allowed: true, reason: "matches read-only command allowlist" };
138
- }
139
-
140
- return { allowed: false, reason: "command is not in the read-only allowlist" };
141
- }
142
-
143
- readOnlyPatterns() {
144
- return [
145
- ...READ_ONLY_COMMAND_PATTERNS,
146
- ...(this.config.extraReadOnlyCommandPatterns ?? [])
147
- ];
148
- }
149
- }
150
-
151
- export class ReadOnlyPolicyDescription {
152
- text() {
153
- return [
154
- "Read-only DevOps policy:",
155
- `- Auto-approved tools: ${READ_ONLY_ALLOWED_TOOLS.join(", ")}`,
156
- `- Always denied tools: ${READ_ONLY_DISALLOWED_TOOLS.join(", ")}`,
157
- "- Bash: denied by default; only explicit read-only command patterns are allowed.",
158
- "- Filesystem sandbox: enabled with denyWrite on all paths.",
159
- "- Permission mode: dontAsk, so unapproved tools fail closed."
160
- ].join("\n");
161
- }
162
- }
163
-
164
- export function isReadOnlyShellCommand(command, config = {}) {
165
- return new ReadOnlyShellPolicy(config).validate(command);
166
- }
167
-
168
- export function describeReadOnlyPolicy() {
169
- return new ReadOnlyPolicyDescription().text();
170
- }