@prism-d1/cli 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/aws/check.js +1 -1
- package/dist/src/aws/connect.d.ts +1 -1
- package/dist/src/aws/connect.d.ts.map +1 -1
- package/dist/src/aws/connect.js.map +1 -1
- package/dist/src/aws/team.d.ts +39 -0
- package/dist/src/aws/team.d.ts.map +1 -0
- package/dist/src/aws/team.js +172 -0
- package/dist/src/aws/team.js.map +1 -0
- package/dist/src/commands/_top/dashboard.d.ts.map +1 -1
- package/dist/src/commands/_top/dashboard.js +22 -3
- package/dist/src/commands/_top/dashboard.js.map +1 -1
- package/dist/src/commands/enable/codeburn.d.ts +6 -2
- package/dist/src/commands/enable/codeburn.d.ts.map +1 -1
- package/dist/src/commands/enable/codeburn.js +126 -21
- package/dist/src/commands/enable/codeburn.js.map +1 -1
- package/dist/src/commands/team/add-developer.d.ts +17 -0
- package/dist/src/commands/team/add-developer.d.ts.map +1 -0
- package/dist/src/commands/team/add-developer.js +22 -0
- package/dist/src/commands/team/add-developer.js.map +1 -0
- package/dist/src/commands/team/info.d.ts +16 -0
- package/dist/src/commands/team/info.d.ts.map +1 -0
- package/dist/src/commands/team/info.js +30 -0
- package/dist/src/commands/team/info.js.map +1 -0
- package/dist/src/dashboard/actions.d.ts +112 -0
- package/dist/src/dashboard/actions.d.ts.map +1 -0
- package/dist/src/dashboard/actions.js +242 -0
- package/dist/src/dashboard/actions.js.map +1 -0
- package/dist/src/dashboard/aws-identity.d.ts +115 -0
- package/dist/src/dashboard/aws-identity.d.ts.map +1 -0
- package/dist/src/dashboard/aws-identity.js +240 -0
- package/dist/src/dashboard/aws-identity.js.map +1 -0
- package/dist/src/dashboard/page.d.ts +1 -1
- package/dist/src/dashboard/page.d.ts.map +1 -1
- package/dist/src/dashboard/page.js +36 -9
- package/dist/src/dashboard/page.js.map +1 -1
- package/dist/src/dashboard/payload.d.ts +7 -0
- package/dist/src/dashboard/payload.d.ts.map +1 -1
- package/dist/src/dashboard/payload.js +2 -0
- package/dist/src/dashboard/payload.js.map +1 -1
- package/dist/src/dashboard/server.d.ts +27 -6
- package/dist/src/dashboard/server.d.ts.map +1 -1
- package/dist/src/dashboard/server.js +75 -10
- package/dist/src/dashboard/server.js.map +1 -1
- package/dist/src/dashboard/setup-api.d.ts +63 -0
- package/dist/src/dashboard/setup-api.d.ts.map +1 -0
- package/dist/src/dashboard/setup-api.js +66 -0
- package/dist/src/dashboard/setup-api.js.map +1 -0
- package/dist/src/dashboard/setup-page.d.ts +16 -0
- package/dist/src/dashboard/setup-page.d.ts.map +1 -0
- package/dist/src/dashboard/setup-page.js +347 -0
- package/dist/src/dashboard/setup-page.js.map +1 -0
- package/dist/src/dashboard/setup.d.ts +11 -2
- package/dist/src/dashboard/setup.d.ts.map +1 -1
- package/dist/src/dashboard/setup.js +35 -16
- package/dist/src/dashboard/setup.js.map +1 -1
- package/dist/src/dashboard/summary.d.ts +1 -1
- package/dist/src/dashboard/summary.d.ts.map +1 -1
- package/dist/src/dashboard/summary.js +2 -1
- package/dist/src/dashboard/summary.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/utils/aws.d.ts +2 -0
- package/dist/src/utils/aws.d.ts.map +1 -1
- package/dist/src/utils/aws.js +1 -1
- package/dist/src/utils/aws.js.map +1 -1
- package/dist/src/utils/exec.d.ts +1 -1
- package/dist/src/utils/exec.d.ts.map +1 -1
- package/dist/src/utils/exec.js +3 -1
- package/dist/src/utils/exec.js.map +1 -1
- package/dist/src/utils/otel-sync-schedule.d.ts +2 -0
- package/dist/src/utils/otel-sync-schedule.d.ts.map +1 -1
- package/dist/src/utils/otel-sync-schedule.js +13 -0
- package/dist/src/utils/otel-sync-schedule.js.map +1 -1
- package/dist/src/utils/sync-mode.d.ts +43 -0
- package/dist/src/utils/sync-mode.d.ts.map +1 -0
- package/dist/src/utils/sync-mode.js +84 -0
- package/dist/src/utils/sync-mode.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Setup page's Run buttons: a fixed allowlist of action ids, each mapped
|
|
3
|
+
* here (server side) to a fixed argv for this CLI's own binary.
|
|
4
|
+
*
|
|
5
|
+
* Safety properties, enforced in start():
|
|
6
|
+
* - spawn(process.execPath, [cli entry, ...fixed args]) with shell:false and
|
|
7
|
+
* stdin closed, so prompts take their no-terminal path and nothing is parsed
|
|
8
|
+
* by a shell;
|
|
9
|
+
* - the request names an action id and nothing else free-form: a repo is an
|
|
10
|
+
* id resolved against the dashboard's own scan list (never a client path),
|
|
11
|
+
* and the only text input is a collector URL for team mode, validated as
|
|
12
|
+
* https with a hostname, no userinfo, at most 2048 characters;
|
|
13
|
+
* - one action at a time, each with a timeout;
|
|
14
|
+
* - output is redacted before it leaves the server, and every run is
|
|
15
|
+
* appended to ~/.prism/logs/dashboard-actions.log (time, id, repo name,
|
|
16
|
+
* exit code; never output or URLs);
|
|
17
|
+
* - no action writes to AWS. aws check and team info are read-only;
|
|
18
|
+
* AWS-writing commands are copy-only on the page.
|
|
19
|
+
*/
|
|
20
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
23
|
+
import { dirname, join } from 'node:path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
const MIN = 60_000;
|
|
26
|
+
export const ACTIONS = {
|
|
27
|
+
'codewatch-repo': { label: 'Enable codewatch in this repo', args: ['enable', 'codewatch'], repo: true, timeoutMs: 2 * MIN },
|
|
28
|
+
'codewatch-global': { label: 'Enable codewatch for every repo', args: ['enable', 'codewatch', '--global'], timeoutMs: 2 * MIN },
|
|
29
|
+
'codeburn-local': { label: 'Keep usage local', args: ['enable', 'codeburn', '--local'], timeoutMs: 2 * MIN },
|
|
30
|
+
'codeburn-systemd': { label: 'Schedule with a systemd user timer', args: ['enable', 'codeburn', '--schedule', 'systemd'], systemd: true, timeoutMs: 5 * MIN },
|
|
31
|
+
// codeburn sync setup opens a browser to sign in, so this one gets longer.
|
|
32
|
+
'codeburn-team': { label: "Sync to my team's PRISM", args: ['enable', 'codeburn', '--url'], url: true, timeoutMs: 10 * MIN },
|
|
33
|
+
'codeburn-remove': { label: 'Remove the sync schedule', args: ['enable', 'codeburn', '--remove'], confirm: true, timeoutMs: 2 * MIN },
|
|
34
|
+
'codeburn-status': { label: 'codeburn status', args: ['enable', 'codeburn', '--status'], timeoutMs: MIN },
|
|
35
|
+
'aws-check': { label: 'AWS account check (read-only)', args: ['aws', 'check'], readOnlyAws: true, timeoutMs: 10 * MIN },
|
|
36
|
+
'team-info': { label: 'Team stack info (read-only)', args: ['team', 'info'], readOnlyAws: true, timeoutMs: 2 * MIN },
|
|
37
|
+
};
|
|
38
|
+
const isActionId = (x) => typeof x === 'string' && Object.prototype.hasOwnProperty.call(ACTIONS, x);
|
|
39
|
+
export const MAX_URL_LENGTH = 2048;
|
|
40
|
+
/** null when `s` is an acceptable collector URL, else why not. */
|
|
41
|
+
export function collectorUrlError(s) {
|
|
42
|
+
if (typeof s !== 'string' || !s)
|
|
43
|
+
return 'a collector URL is required';
|
|
44
|
+
if (s.length > MAX_URL_LENGTH)
|
|
45
|
+
return `the URL is longer than ${MAX_URL_LENGTH} characters`;
|
|
46
|
+
if (/[\s\u0000-\u001f\u007f]/.test(s))
|
|
47
|
+
return 'the URL contains spaces or control characters';
|
|
48
|
+
let u;
|
|
49
|
+
try {
|
|
50
|
+
u = new URL(s);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return 'not a valid URL';
|
|
54
|
+
}
|
|
55
|
+
if (u.protocol !== 'https:')
|
|
56
|
+
return 'the URL must start with https://';
|
|
57
|
+
if (!u.hostname)
|
|
58
|
+
return 'the URL has no hostname';
|
|
59
|
+
if (u.username || u.password || /^https:\/\/[^/?#]*@/i.test(s))
|
|
60
|
+
return 'the URL must not contain a user name or password';
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/** Stable id for a scanned repo: never a path the client could choose. */
|
|
64
|
+
export function repoId(path) {
|
|
65
|
+
return createHash('sha256').update(path).digest('hex').slice(0, 16);
|
|
66
|
+
}
|
|
67
|
+
const ANSI_RE = /\u001b\[[0-9;?]*[A-Za-z]/g;
|
|
68
|
+
/**
|
|
69
|
+
* Masks secrets in command output: AWS key ids, key=value secrets, bearer
|
|
70
|
+
* tokens, JWTs, URL userinfo and token-like query parameters.
|
|
71
|
+
* shortcut: pattern-based, so an unusual secret format can slip through; add its pattern when one is seen.
|
|
72
|
+
*/
|
|
73
|
+
export function redact(s) {
|
|
74
|
+
return s
|
|
75
|
+
.replace(ANSI_RE, '')
|
|
76
|
+
.replace(/\b(?:AKIA|ASIA|ABIA|ACCA|A3T[A-Z0-9])[A-Z0-9]{16}\b/g, '[REDACTED]')
|
|
77
|
+
.replace(/\b((?:aws_)?(?:secret_access_key|session_token|security_token|secret|password|passwd|api[_-]?key|access_token|refresh_token|id_token|client_secret)["']?\s*[:=]\s*["']?)[^\s"',;&]+/gi, '$1[REDACTED]')
|
|
78
|
+
.replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{8,}/g, '$1 [REDACTED]')
|
|
79
|
+
.replace(/\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}/g, '[REDACTED]')
|
|
80
|
+
.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+@/gi, '$1[REDACTED]@')
|
|
81
|
+
.replace(/([?&#](?:token|access_token|id_token|code|signature|sig|key|x-amz-[a-z-]+)=)[^&\s#"']+/gi, '$1[REDACTED]');
|
|
82
|
+
}
|
|
83
|
+
/** This CLI's own entry script: dist/bin/prism-cli.js beside dist/src/dashboard/. */
|
|
84
|
+
export function cliEntry() {
|
|
85
|
+
return fileURLToPath(new URL('../../bin/prism-cli.js', import.meta.url));
|
|
86
|
+
}
|
|
87
|
+
const MAX_OUTPUT = 256 * 1024;
|
|
88
|
+
const ALLOWED_KEYS = new Set(['action', 'repo', 'url', 'confirm']);
|
|
89
|
+
/** `prism-cli <args>` as shown on the page. Arguments are fixed words or the validated https URL. */
|
|
90
|
+
export function displayCommand(args) {
|
|
91
|
+
return ['prism-cli', ...args.map((a) => (/^[A-Za-z0-9_./:=@%+,-]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`))].join(' ');
|
|
92
|
+
}
|
|
93
|
+
export function actionLogPath(home) {
|
|
94
|
+
return join(home, '.prism', 'logs', 'dashboard-actions.log');
|
|
95
|
+
}
|
|
96
|
+
/** Repo names go into a one-line log: anything but name characters becomes _. */
|
|
97
|
+
const logSafe = (s) => s.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 120);
|
|
98
|
+
export class ActionRunner {
|
|
99
|
+
d;
|
|
100
|
+
cur = null;
|
|
101
|
+
busy = false;
|
|
102
|
+
seq = 0;
|
|
103
|
+
constructor(d) {
|
|
104
|
+
this.d = d;
|
|
105
|
+
}
|
|
106
|
+
/** True while an action (or Check now) holds the lock. */
|
|
107
|
+
get running() { return this.busy; }
|
|
108
|
+
/** Runs `fn` under the same one-at-a-time lock as the actions. */
|
|
109
|
+
async exclusive(fn) {
|
|
110
|
+
if (this.busy)
|
|
111
|
+
return { status: 409, json: { error: 'another action is running; wait for it to finish' } };
|
|
112
|
+
this.busy = true;
|
|
113
|
+
try {
|
|
114
|
+
return { status: 200, json: await fn() };
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
this.busy = false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
available(id) {
|
|
121
|
+
return !ACTIONS[id].systemd || (this.d.platform === 'linux' && this.d.systemdAvailable());
|
|
122
|
+
}
|
|
123
|
+
/** Validates the request, then starts the action. 202 with the command, else 4xx with the reason. */
|
|
124
|
+
async start(body) {
|
|
125
|
+
const bad = (error, status = 400) => ({ status, json: { error } });
|
|
126
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
127
|
+
return bad('expected a JSON object');
|
|
128
|
+
const b = body;
|
|
129
|
+
const extra = Object.keys(b).filter((k) => !ALLOWED_KEYS.has(k));
|
|
130
|
+
if (extra.length)
|
|
131
|
+
return bad(`unexpected field ${extra[0]}`);
|
|
132
|
+
if (!isActionId(b.action))
|
|
133
|
+
return bad('unknown action');
|
|
134
|
+
const id = b.action, def = ACTIONS[id];
|
|
135
|
+
if (!this.available(id))
|
|
136
|
+
return bad('this action is not available on this machine');
|
|
137
|
+
let repo = null;
|
|
138
|
+
if (def.repo) {
|
|
139
|
+
if (typeof b.repo !== 'string' || !/^[a-f0-9]{16}$/.test(b.repo))
|
|
140
|
+
return bad('repo must be an id from the scan list');
|
|
141
|
+
repo = (await this.d.repos()).find((r) => r.id === b.repo) ?? null;
|
|
142
|
+
if (!repo)
|
|
143
|
+
return bad('repo is not in the scan list');
|
|
144
|
+
}
|
|
145
|
+
else if (b.repo !== undefined)
|
|
146
|
+
return bad('this action does not take a repo');
|
|
147
|
+
if (def.url) {
|
|
148
|
+
const e = collectorUrlError(b.url);
|
|
149
|
+
if (e)
|
|
150
|
+
return bad(`collector URL: ${e}`);
|
|
151
|
+
}
|
|
152
|
+
else if (b.url !== undefined)
|
|
153
|
+
return bad('this action does not take a URL');
|
|
154
|
+
if (def.confirm && b.confirm !== true)
|
|
155
|
+
return bad('this action needs confirm: true');
|
|
156
|
+
if (b.confirm !== undefined && typeof b.confirm !== 'boolean')
|
|
157
|
+
return bad('confirm must be true or false');
|
|
158
|
+
if (this.busy)
|
|
159
|
+
return bad('another action is running; wait for it to finish', 409);
|
|
160
|
+
const args = [...def.args, ...(def.url ? [b.url] : [])];
|
|
161
|
+
const cwd = repo ? repo.path : this.d.cwd;
|
|
162
|
+
const now = this.d.now ?? (() => new Date());
|
|
163
|
+
this.busy = true;
|
|
164
|
+
const runId = ++this.seq;
|
|
165
|
+
const st = this.cur = {
|
|
166
|
+
state: 'running', runId, action: id, repo: repo?.name ?? null, command: displayCommand(args), cwd,
|
|
167
|
+
buf: '', exitCode: null, timedOut: false, startedAt: now().toISOString(), finishedAt: null,
|
|
168
|
+
};
|
|
169
|
+
const add = (chunk) => {
|
|
170
|
+
st.buf += chunk.toString();
|
|
171
|
+
if (st.buf.length > MAX_OUTPUT)
|
|
172
|
+
st.buf = '[earlier output trimmed]\n' + st.buf.slice(-MAX_OUTPUT);
|
|
173
|
+
};
|
|
174
|
+
let child;
|
|
175
|
+
try {
|
|
176
|
+
child = (this.d.spawn ?? nodeSpawn)(this.d.execPath, [this.d.entry, ...args], {
|
|
177
|
+
cwd, shell: false, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'],
|
|
178
|
+
env: { ...process.env, NO_COLOR: '1', FORCE_COLOR: '0' },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
add(`could not start: ${e.message}\n`);
|
|
183
|
+
this.finish(st, 1);
|
|
184
|
+
return { status: 202, json: this.status() };
|
|
185
|
+
}
|
|
186
|
+
child.stdout?.on('data', add);
|
|
187
|
+
child.stderr?.on('data', add);
|
|
188
|
+
let killer;
|
|
189
|
+
const timer = setTimeout(() => {
|
|
190
|
+
st.timedOut = true;
|
|
191
|
+
add(`\n[timed out after ${Math.round(def.timeoutMs / 1000)} s; stopping it]\n`);
|
|
192
|
+
child.kill('SIGTERM');
|
|
193
|
+
killer = setTimeout(() => child.kill('SIGKILL'), this.d.killGraceMs ?? 5000);
|
|
194
|
+
killer.unref();
|
|
195
|
+
}, def.timeoutMs);
|
|
196
|
+
timer.unref();
|
|
197
|
+
let settled = false;
|
|
198
|
+
const done = (code) => {
|
|
199
|
+
if (settled)
|
|
200
|
+
return;
|
|
201
|
+
settled = true;
|
|
202
|
+
clearTimeout(timer);
|
|
203
|
+
if (killer)
|
|
204
|
+
clearTimeout(killer);
|
|
205
|
+
this.finish(st, st.timedOut ? 124 : code ?? 1);
|
|
206
|
+
};
|
|
207
|
+
child.on('error', (e) => { add(`could not start: ${e.message}\n`); done(1); });
|
|
208
|
+
child.on('close', (code) => done(code));
|
|
209
|
+
return { status: 202, json: this.status() };
|
|
210
|
+
}
|
|
211
|
+
finish(st, code) {
|
|
212
|
+
st.exitCode = code;
|
|
213
|
+
st.state = 'done';
|
|
214
|
+
st.finishedAt = (this.d.now ?? (() => new Date()))().toISOString();
|
|
215
|
+
this.busy = false;
|
|
216
|
+
this.log(st);
|
|
217
|
+
try {
|
|
218
|
+
this.d.onDone?.();
|
|
219
|
+
}
|
|
220
|
+
catch { /* a refresh hook must not break the runner */ }
|
|
221
|
+
}
|
|
222
|
+
log(st) {
|
|
223
|
+
const line = JSON.stringify({ time: st.finishedAt, action: st.action, repo: st.repo ? logSafe(st.repo) : null, exit: st.exitCode }) + '\n';
|
|
224
|
+
const p = actionLogPath(this.d.home);
|
|
225
|
+
try {
|
|
226
|
+
mkdirSync(dirname(p), { recursive: true, mode: 0o700 });
|
|
227
|
+
appendFileSync(p, line, { mode: 0o600 });
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
process.stderr.write(`prism dashboard: could not write ${p}: ${e.message}\n`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/** The current or last action, output redacted. */
|
|
234
|
+
status() {
|
|
235
|
+
const c = this.cur;
|
|
236
|
+
if (!c)
|
|
237
|
+
return { state: 'idle' };
|
|
238
|
+
const { buf, ...rest } = c;
|
|
239
|
+
return { ...rest, output: redact(buf) };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../../src/dashboard/actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,KAAK,IAAI,SAAS,EAAwC,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAsBzC,MAAM,GAAG,GAAG,MAAM,CAAC;AACnB,MAAM,CAAC,MAAM,OAAO,GAAgC;IAClD,gBAAgB,EAAE,EAAE,KAAK,EAAE,+BAA+B,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,GAAG,GAAG,EAAE;IAC3H,kBAAkB,EAAE,EAAE,KAAK,EAAE,iCAAiC,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,GAAG,EAAE;IAC/H,gBAAgB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,GAAG,EAAE;IAC5G,kBAAkB,EAAE,EAAE,KAAK,EAAE,oCAAoC,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,GAAG,GAAG,EAAE;IAC7J,2EAA2E;IAC3E,eAAe,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,GAAG,EAAE;IAC5H,iBAAiB,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,GAAG,GAAG,EAAE;IACrI,iBAAiB,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE;IACzG,WAAW,EAAE,EAAE,KAAK,EAAE,+BAA+B,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,GAAG,EAAE;IACvH,WAAW,EAAE,EAAE,KAAK,EAAE,6BAA6B,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,GAAG,GAAG,EAAE;CACrH,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,CAAU,EAAiB,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAE5H,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,CAAU;IAC1C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC;QAAE,OAAO,6BAA6B,CAAC;IACtE,IAAI,CAAC,CAAC,MAAM,GAAG,cAAc;QAAE,OAAO,0BAA0B,cAAc,aAAa,CAAC;IAC5F,IAAI,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,+CAA+C,CAAC;IAC9F,IAAI,CAAM,CAAC;IACX,IAAI,CAAC;QAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,iBAAiB,CAAC;IAAC,CAAC;IAC3D,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,kCAAkC,CAAC;IACvE,IAAI,CAAC,CAAC,CAAC,QAAQ;QAAE,OAAO,yBAAyB,CAAC;IAClD,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,kDAAkD,CAAC;IAC1H,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,OAAO,GAAG,2BAA2B,CAAC;AAE5C;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,OAAO,CAAC;SACL,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,OAAO,CAAC,sDAAsD,EAAE,YAAY,CAAC;SAC7E,OAAO,CAAC,uLAAuL,EAAE,cAAc,CAAC;SAChN,OAAO,CAAC,4CAA4C,EAAE,eAAe,CAAC;SACtE,OAAO,CAAC,+DAA+D,EAAE,YAAY,CAAC;SACtF,OAAO,CAAC,uCAAuC,EAAE,eAAe,CAAC;SACjE,OAAO,CAAC,0FAA0F,EAAE,cAAc,CAAC,CAAC;AACzH,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,QAAQ;IACtB,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,wBAAwB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3E,CAAC;AAuCD,MAAM,UAAU,GAAG,GAAG,GAAG,IAAI,CAAC;AAC9B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;AAEnE,qGAAqG;AACrG,MAAM,UAAU,cAAc,CAAC,IAAuB;IACpD,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjI,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,uBAAuB,CAAC,CAAC;AAC/D,CAAC;AAED,iFAAiF;AACjF,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAEhF,MAAM,OAAO,YAAY;IAIH;IAHZ,GAAG,GAA4C,IAAI,CAAC;IACpD,IAAI,GAAG,KAAK,CAAC;IACb,GAAG,GAAG,CAAC,CAAC;IAChB,YAAoB,CAAa;QAAb,MAAC,GAAD,CAAC,CAAY;IAAG,CAAC;IAErC,0DAA0D;IAC1D,IAAI,OAAO,KAAc,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAE5C,kEAAkE;IAClE,KAAK,CAAC,SAAS,CAAI,EAAwB;QACzC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,kDAAkD,EAAE,EAAE,CAAC;QAC3G,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAAC,CAAC;gBAAS,CAAC;YAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QAAC,CAAC;IAClF,CAAC;IAED,SAAS,CAAC,EAAY;QACpB,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,qGAAqG;IACrG,KAAK,CAAC,KAAK,CAAC,IAAa;QACvB,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,MAAM,GAAG,GAAG,EAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACtF,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACnG,MAAM,CAAC,GAAG,IAA+B,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,IAAI,KAAK,CAAC,MAAM;YAAE,OAAO,GAAG,CAAC,oBAAoB,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,OAAO,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACxD,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAAE,OAAO,GAAG,CAAC,8CAA8C,CAAC,CAAC;QACpF,IAAI,IAAI,GAAoB,IAAI,CAAC;QACjC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACb,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;gBAAE,OAAO,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACtH,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;YACnE,IAAI,CAAC,IAAI;gBAAE,OAAO,GAAG,CAAC,8BAA8B,CAAC,CAAC;QACxD,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAChF,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,MAAM,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,CAAC;gBAAE,OAAO,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC,iCAAiC,CAAC,CAAC;QAC9E,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,GAAG,CAAC,iCAAiC,CAAC,CAAC;QACrF,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC3G,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,GAAG,CAAC,kDAAkD,EAAE,GAAG,CAAC,CAAC;QAEnF,MAAM,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,MAAM,KAAK,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG;YACpB,KAAK,EAAE,SAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,GAAG;YAC1G,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,IAAI;SACzD,CAAC;QACpC,MAAM,GAAG,GAAG,CAAC,KAAsB,EAAE,EAAE;YACrC,EAAE,CAAC,GAAG,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC3B,IAAI,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,UAAU;gBAAE,EAAE,CAAC,GAAG,GAAG,4BAA4B,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC;QACpG,CAAC,CAAC;QACF,IAAI,KAAmB,CAAC;QACxB,IAAI,CAAC;YACH,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE;gBAC5E,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;gBACvE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE;aACzD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,GAAG,CAAC,oBAAqB,CAAW,CAAC,OAAO,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACnB,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9C,CAAC;QACD,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,IAAI,MAAkC,CAAC;QACvC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,EAAE,CAAC,QAAQ,GAAG,IAAI,CAAC;YACnB,GAAG,CAAC,sBAAsB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAChF,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtB,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;YAC7E,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QAClB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,IAAI,GAAG,CAAC,IAAmB,EAAE,EAAE;YACnC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,MAAM;gBAAE,YAAY,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IAC9C,CAAC;IAEO,MAAM,CAAC,EAAkC,EAAE,IAAY;QAC7D,EAAE,CAAC,QAAQ,GAAG,IAAI,CAAC;QACnB,EAAE,CAAC,KAAK,GAAG,MAAM,CAAC;QAClB,EAAE,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QAClB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,IAAI,CAAC;YAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,8CAA8C,CAAC,CAAC;IACrF,CAAC;IAEO,GAAG,CAAC,EAAgB;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC;QAC3I,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACxD,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,KAAM,CAAW,CAAC,OAAO,IAAI,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,MAAM;QACJ,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC;QACnB,IAAI,CAAC,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;QAC3B,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { aws } from '../utils/aws.js';
|
|
2
|
+
import { writeConnectionFile, type SavedConnection } from '../aws/connection.js';
|
|
3
|
+
/** Section name -> its key names (lower case), plus the region value: the only value kept. */
|
|
4
|
+
export type IniKeys = Map<string, {
|
|
5
|
+
keys: Set<string>;
|
|
6
|
+
region?: string;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* Key names per section. Values are discarded line by line; `region` is kept
|
|
10
|
+
* only when it looks like a region name. Indented lines are nested settings
|
|
11
|
+
* (e.g. s3 = ...) and are skipped.
|
|
12
|
+
*/
|
|
13
|
+
export declare function iniKeys(text: string, kind: 'config' | 'credentials'): IniKeys;
|
|
14
|
+
export type CredentialType = 'credential_process' | 'sso' | 'static-credentials-file' | 'static-config-file' | 'assume-role' | 'web-identity' | 'env' | 'container' | 'none';
|
|
15
|
+
export declare const CREDENTIAL_TYPE_LABEL: Record<CredentialType, string>;
|
|
16
|
+
/**
|
|
17
|
+
* The type the AWS CLI would use for `profile`, in its provider order within a
|
|
18
|
+
* profile: role_arn, web identity, SSO, keys in the credentials file,
|
|
19
|
+
* credential_process, keys in the config file. `shadowed` lists the types that
|
|
20
|
+
* are configured but lose.
|
|
21
|
+
*/
|
|
22
|
+
export declare function classifyProfile(profile: string, config: IniKeys, creds: IniKeys): {
|
|
23
|
+
type: CredentialType;
|
|
24
|
+
shadowed: CredentialType[];
|
|
25
|
+
};
|
|
26
|
+
export interface ResolutionStep {
|
|
27
|
+
id: 'flags' | 'env' | 'repo' | 'user' | 'default';
|
|
28
|
+
label: string;
|
|
29
|
+
/** used: this step chose the credentials. skipped: set, but an earlier step won. */
|
|
30
|
+
state: 'used' | 'skipped' | 'not-set' | 'n/a';
|
|
31
|
+
detail: string;
|
|
32
|
+
}
|
|
33
|
+
export interface AwsIdentityView {
|
|
34
|
+
/** Profile name the AWS CLI is given, or null (env keys, container, or nothing). */
|
|
35
|
+
profile: string | null;
|
|
36
|
+
/** Why: "AWS_PROFILE", "aws connect, repo", "default chain", ... */
|
|
37
|
+
source: string;
|
|
38
|
+
credentialType: CredentialType;
|
|
39
|
+
credentialLabel: string;
|
|
40
|
+
region: {
|
|
41
|
+
value: string;
|
|
42
|
+
source: string;
|
|
43
|
+
} | null;
|
|
44
|
+
steps: ResolutionStep[];
|
|
45
|
+
/** The saved aws connect connection (the repo file, else the user file), whether or not it is in use. */
|
|
46
|
+
saved: null | {
|
|
47
|
+
scope: 'repo' | 'user';
|
|
48
|
+
path: string;
|
|
49
|
+
profile: string;
|
|
50
|
+
region: string;
|
|
51
|
+
account: string;
|
|
52
|
+
alias: string | null;
|
|
53
|
+
verifiedAt: string;
|
|
54
|
+
inUse: boolean;
|
|
55
|
+
};
|
|
56
|
+
warnings: string[];
|
|
57
|
+
files: {
|
|
58
|
+
config: string;
|
|
59
|
+
credentials: string;
|
|
60
|
+
configFound: boolean;
|
|
61
|
+
credentialsFound: boolean;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export interface IdentityDeps {
|
|
65
|
+
env: NodeJS.ProcessEnv;
|
|
66
|
+
home: string;
|
|
67
|
+
/** The repo the dashboard runs in (for .prism/aws.local.json), or null. */
|
|
68
|
+
repoRoot: string | null;
|
|
69
|
+
/** File reader; null when the file is missing or unreadable. */
|
|
70
|
+
readText?: (path: string) => string | null;
|
|
71
|
+
}
|
|
72
|
+
export declare const STATIC_DEFAULT_WARNING = "The default profile has static keys in ~/.aws/credentials. Static keys win over a credential_process or SSO setting for the same profile, and they often expire. If you sign in with credential_process or SSO, remove the keys from [default] in ~/.aws/credentials.";
|
|
73
|
+
export declare const NO_CREDENTIALS_WARNING = "No AWS credentials found: no AWS env var, no saved aws connect profile, and no default profile in ~/.aws/config or ~/.aws/credentials. Set one up with aws configure sso (or aws configure), then run prism-cli aws connect.";
|
|
74
|
+
/** What prism-cli would use, from local files and env only. No AWS call, no aws CLI call. */
|
|
75
|
+
export declare function resolveAwsIdentity(d: IdentityDeps): AwsIdentityView;
|
|
76
|
+
export type CheckNowResult = {
|
|
77
|
+
ok: true;
|
|
78
|
+
account: string;
|
|
79
|
+
arn: string;
|
|
80
|
+
region: string | null;
|
|
81
|
+
profile: string | null;
|
|
82
|
+
savedAccount: string | null;
|
|
83
|
+
matches: boolean | null;
|
|
84
|
+
updated: boolean;
|
|
85
|
+
note: string;
|
|
86
|
+
} | {
|
|
87
|
+
ok: false;
|
|
88
|
+
expired: boolean;
|
|
89
|
+
error: string;
|
|
90
|
+
profile: string | null;
|
|
91
|
+
region: string | null;
|
|
92
|
+
fixes: string[];
|
|
93
|
+
};
|
|
94
|
+
export interface CheckNowDeps {
|
|
95
|
+
identity: AwsIdentityView;
|
|
96
|
+
/** The saved connection behind identity.saved (with its real path), or null. */
|
|
97
|
+
saved: SavedConnection | null;
|
|
98
|
+
/** Same shape as aws connect's deps.aws: one read-only `aws <args>` call. */
|
|
99
|
+
aws(args: string[], profile: string, region: string): ReturnType<typeof aws>;
|
|
100
|
+
now(): Date;
|
|
101
|
+
write?: typeof writeConnectionFile;
|
|
102
|
+
redact(s: string): string;
|
|
103
|
+
}
|
|
104
|
+
/** The fixes to suggest for a failed check, by credential type. */
|
|
105
|
+
export declare function checkFixes(v: AwsIdentityView): string[];
|
|
106
|
+
/** The default `aws` call for Check now: aws connect's call, with the region optional and a timeout. */
|
|
107
|
+
export declare const CHECK_NOW_TIMEOUT_MS = 30000;
|
|
108
|
+
export declare function checkNowAws(args: string[], profile: string, region: string): import("../utils/aws.js").AwsResult;
|
|
109
|
+
/**
|
|
110
|
+
* One read-only `sts get-caller-identity`, through aws connect's callerIdentity.
|
|
111
|
+
* On a match with the saved connection (same profile and region), the saved
|
|
112
|
+
* file's verifiedAt is refreshed; nothing else is written.
|
|
113
|
+
*/
|
|
114
|
+
export declare function checkNow(d: CheckNowDeps): CheckNowResult;
|
|
115
|
+
//# sourceMappingURL=aws-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-identity.d.ts","sourceRoot":"","sources":["../../../src/dashboard/aws-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Setup page's AWS section: which credentials prism-cli would use and why,
|
|
3
|
+
* with no AWS call, and the one read-only "Check now" call.
|
|
4
|
+
*
|
|
5
|
+
* Resolution follows the order every AWS command uses (aws/connection.ts):
|
|
6
|
+
* flags (none here), env credentials, .prism/aws.local.json, ~/.prism/aws.json,
|
|
7
|
+
* then the default chain. The credential TYPE comes from the KEY NAMES in
|
|
8
|
+
* ~/.aws/config and ~/.aws/credentials. The parser drops every value as it
|
|
9
|
+
* reads a line, except `region`, so no secret is kept, returned or shown.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { aws } from '../utils/aws.js';
|
|
14
|
+
import { callerIdentity } from '../aws/connect.js';
|
|
15
|
+
import { applyConnection, connectionLabel, loadConnection, REGION_RE, writeConnectionFile } from '../aws/connection.js';
|
|
16
|
+
import { resolveRegion } from '../security/setup.js';
|
|
17
|
+
import { tildeHome } from './payload.js';
|
|
18
|
+
/**
|
|
19
|
+
* Key names per section. Values are discarded line by line; `region` is kept
|
|
20
|
+
* only when it looks like a region name. Indented lines are nested settings
|
|
21
|
+
* (e.g. s3 = ...) and are skipped.
|
|
22
|
+
*/
|
|
23
|
+
export function iniKeys(text, kind) {
|
|
24
|
+
const out = new Map();
|
|
25
|
+
let cur = null;
|
|
26
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
27
|
+
if (/^\s/.test(raw))
|
|
28
|
+
continue;
|
|
29
|
+
const line = raw.trim();
|
|
30
|
+
if (!line || line.startsWith('#') || line.startsWith(';'))
|
|
31
|
+
continue;
|
|
32
|
+
const sec = /^\[\s*([^\]]+?)\s*\]$/.exec(line);
|
|
33
|
+
if (sec) {
|
|
34
|
+
// config: [default] and [profile name]; [sso-session x] and others are not profiles.
|
|
35
|
+
const name = kind === 'credentials' ? sec[1] : sec[1] === 'default' ? 'default' : /^profile\s+(.+)$/.exec(sec[1])?.[1]?.trim();
|
|
36
|
+
cur = name ? (out.get(name) ?? { keys: new Set() }) : null;
|
|
37
|
+
if (name && cur)
|
|
38
|
+
out.set(name, cur);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const eq = line.indexOf('=');
|
|
42
|
+
if (!cur || eq <= 0)
|
|
43
|
+
continue;
|
|
44
|
+
const key = line.slice(0, eq).trim().toLowerCase();
|
|
45
|
+
cur.keys.add(key);
|
|
46
|
+
if (key === 'region') {
|
|
47
|
+
const v = line.slice(eq + 1).trim();
|
|
48
|
+
if (REGION_RE.test(v))
|
|
49
|
+
cur.region = v;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
export const CREDENTIAL_TYPE_LABEL = {
|
|
55
|
+
credential_process: 'credential_process',
|
|
56
|
+
sso: 'IAM Identity Center (SSO)',
|
|
57
|
+
'static-credentials-file': 'static keys in ~/.aws/credentials',
|
|
58
|
+
'static-config-file': 'static keys in ~/.aws/config',
|
|
59
|
+
'assume-role': 'assume role (role_arn)',
|
|
60
|
+
'web-identity': 'web identity token',
|
|
61
|
+
env: 'environment variables',
|
|
62
|
+
container: 'container credentials',
|
|
63
|
+
none: 'none found',
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* The type the AWS CLI would use for `profile`, in its provider order within a
|
|
67
|
+
* profile: role_arn, web identity, SSO, keys in the credentials file,
|
|
68
|
+
* credential_process, keys in the config file. `shadowed` lists the types that
|
|
69
|
+
* are configured but lose.
|
|
70
|
+
*/
|
|
71
|
+
export function classifyProfile(profile, config, creds) {
|
|
72
|
+
const c = config.get(profile)?.keys ?? new Set();
|
|
73
|
+
const k = creds.get(profile)?.keys ?? new Set();
|
|
74
|
+
const present = [];
|
|
75
|
+
if (c.has('role_arn') && (c.has('web_identity_token_file')))
|
|
76
|
+
present.push('web-identity');
|
|
77
|
+
else if (c.has('role_arn') || k.has('role_arn'))
|
|
78
|
+
present.push('assume-role');
|
|
79
|
+
if (c.has('sso_session') || c.has('sso_start_url') || c.has('sso_account_id'))
|
|
80
|
+
present.push('sso');
|
|
81
|
+
if (k.has('aws_access_key_id'))
|
|
82
|
+
present.push('static-credentials-file');
|
|
83
|
+
if (c.has('credential_process') || k.has('credential_process'))
|
|
84
|
+
present.push('credential_process');
|
|
85
|
+
if (c.has('aws_access_key_id'))
|
|
86
|
+
present.push('static-config-file');
|
|
87
|
+
if (!present.length)
|
|
88
|
+
return { type: 'none', shadowed: [] };
|
|
89
|
+
return { type: present[0], shadowed: present.slice(1) };
|
|
90
|
+
}
|
|
91
|
+
const readTextFile = (p) => { try {
|
|
92
|
+
return readFileSync(p, 'utf8');
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
} };
|
|
97
|
+
export const STATIC_DEFAULT_WARNING = 'The default profile has static keys in ~/.aws/credentials. Static keys win over a credential_process or SSO setting for the same profile, and they often expire. If you sign in with credential_process or SSO, remove the keys from [default] in ~/.aws/credentials.';
|
|
98
|
+
export const NO_CREDENTIALS_WARNING = 'No AWS credentials found: no AWS env var, no saved aws connect profile, and no default profile in ~/.aws/config or ~/.aws/credentials. Set one up with aws configure sso (or aws configure), then run prism-cli aws connect.';
|
|
99
|
+
/** What prism-cli would use, from local files and env only. No AWS call, no aws CLI call. */
|
|
100
|
+
export function resolveAwsIdentity(d) {
|
|
101
|
+
const read = d.readText ?? readTextFile;
|
|
102
|
+
const env = d.env;
|
|
103
|
+
const configPath = env.AWS_CONFIG_FILE || join(d.home, '.aws', 'config');
|
|
104
|
+
const credsPath = env.AWS_SHARED_CREDENTIALS_FILE || join(d.home, '.aws', 'credentials');
|
|
105
|
+
const configText = read(configPath), credsText = read(credsPath);
|
|
106
|
+
const config = iniKeys(configText ?? '', 'config');
|
|
107
|
+
const creds = iniKeys(credsText ?? '', 'credentials');
|
|
108
|
+
const saved = loadConnection(d.home, d.repoRoot);
|
|
109
|
+
const applied = applyConnection({}, saved, env);
|
|
110
|
+
const warnings = [];
|
|
111
|
+
let profile = null, source, type, shadowed = [];
|
|
112
|
+
let envDetail = 'no AWS credential env var set';
|
|
113
|
+
let decided;
|
|
114
|
+
if (env.AWS_ACCESS_KEY_ID) {
|
|
115
|
+
type = 'env';
|
|
116
|
+
source = 'AWS_ACCESS_KEY_ID';
|
|
117
|
+
decided = 'env';
|
|
118
|
+
envDetail = `AWS_ACCESS_KEY_ID is set${env.AWS_PROFILE ? ' (it wins over AWS_PROFILE)' : ''}`;
|
|
119
|
+
}
|
|
120
|
+
else if (env.AWS_PROFILE) {
|
|
121
|
+
profile = env.AWS_PROFILE;
|
|
122
|
+
source = 'AWS_PROFILE';
|
|
123
|
+
decided = 'env';
|
|
124
|
+
({ type, shadowed } = classifyProfile(profile, config, creds));
|
|
125
|
+
envDetail = `AWS_PROFILE=${profile}`;
|
|
126
|
+
}
|
|
127
|
+
else if (env.AWS_WEB_IDENTITY_TOKEN_FILE) {
|
|
128
|
+
type = 'web-identity';
|
|
129
|
+
source = 'AWS_WEB_IDENTITY_TOKEN_FILE';
|
|
130
|
+
decided = 'env';
|
|
131
|
+
envDetail = 'AWS_WEB_IDENTITY_TOKEN_FILE is set';
|
|
132
|
+
}
|
|
133
|
+
else if (env.AWS_CONTAINER_CREDENTIALS_FULL_URI || env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
|
|
134
|
+
type = 'container';
|
|
135
|
+
source = 'container credentials env var';
|
|
136
|
+
decided = 'env';
|
|
137
|
+
envDetail = 'a container credentials env var is set';
|
|
138
|
+
}
|
|
139
|
+
else if (saved && applied.profileSource) {
|
|
140
|
+
profile = saved.profile;
|
|
141
|
+
source = connectionLabel(saved);
|
|
142
|
+
decided = saved.scope;
|
|
143
|
+
({ type, shadowed } = classifyProfile(profile, config, creds));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
profile = 'default';
|
|
147
|
+
source = 'default chain';
|
|
148
|
+
decided = 'default';
|
|
149
|
+
({ type, shadowed } = classifyProfile(profile, config, creds));
|
|
150
|
+
}
|
|
151
|
+
const repoFile = d.repoRoot ? join(d.repoRoot, '.prism', 'aws.local.json') : null;
|
|
152
|
+
const savedStep = (scope) => decided === scope ? 'used' : saved?.scope === scope ? 'skipped' : 'not-set';
|
|
153
|
+
const steps = [
|
|
154
|
+
{ id: 'flags', label: '--profile / --region', state: 'n/a', detail: 'not used by the dashboard' },
|
|
155
|
+
{ id: 'env', label: 'Environment', state: decided === 'env' ? 'used' : 'not-set', detail: envDetail },
|
|
156
|
+
{ id: 'repo', label: 'Repo: .prism/aws.local.json', state: savedStep('repo'),
|
|
157
|
+
detail: !repoFile ? 'the dashboard is not running in a git repo' : saved?.scope === 'repo' ? `profile ${saved.profile}` : 'not set' },
|
|
158
|
+
{ id: 'user', label: 'User: ~/.prism/aws.json', state: savedStep('user'),
|
|
159
|
+
detail: saved?.scope === 'user' ? `profile ${saved.profile}` : saved?.scope === 'repo' ? 'the repo file wins' : 'not set' },
|
|
160
|
+
{ id: 'default', label: 'Default chain', state: decided === 'default' ? 'used' : 'not-set', detail: 'the [default] profile' },
|
|
161
|
+
];
|
|
162
|
+
const configRegion = (p) => config.get(p ?? env.AWS_PROFILE ?? 'default')?.region ?? null;
|
|
163
|
+
const region = resolveRegion({ profile: profile && profile !== 'default' ? profile : undefined, connection: applied.connection }, { env, configRegion });
|
|
164
|
+
if (creds.get('default')?.keys.has('aws_access_key_id'))
|
|
165
|
+
warnings.push(STATIC_DEFAULT_WARNING);
|
|
166
|
+
if (profile && profile !== 'default' && type === 'static-credentials-file' && shadowed.length) {
|
|
167
|
+
warnings.push(`Profile ${profile} has static keys in ~/.aws/credentials, and they win over its ${shadowed.map((t) => CREDENTIAL_TYPE_LABEL[t]).join(' and ')}. Remove the keys if they are stale.`);
|
|
168
|
+
}
|
|
169
|
+
if (type === 'none') {
|
|
170
|
+
warnings.push(profile && profile !== 'default'
|
|
171
|
+
? `Profile ${profile} (${source}) is not configured in ~/.aws/config or ~/.aws/credentials. Create it with aws configure sso --profile ${profile}, or re-run prism-cli aws connect.`
|
|
172
|
+
: NO_CREDENTIALS_WARNING);
|
|
173
|
+
}
|
|
174
|
+
if (saved && !applied.profileSource)
|
|
175
|
+
warnings.push(`The saved aws connect profile ${saved.profile} is not used: ${source} is set in the dashboard's environment and wins.`);
|
|
176
|
+
return {
|
|
177
|
+
profile, source, credentialType: type, credentialLabel: CREDENTIAL_TYPE_LABEL[type],
|
|
178
|
+
region: region ? { value: region.region, source: region.source } : null,
|
|
179
|
+
steps,
|
|
180
|
+
saved: saved ? {
|
|
181
|
+
scope: saved.scope, path: tildeHome(saved.path, d.home), profile: saved.profile, region: saved.region, account: saved.account,
|
|
182
|
+
alias: saved.alias, verifiedAt: saved.verifiedAt, inUse: !!applied.profileSource,
|
|
183
|
+
} : null,
|
|
184
|
+
warnings,
|
|
185
|
+
files: { config: tildeHome(configPath, d.home), credentials: tildeHome(credsPath, d.home), configFound: configText !== null, credentialsFound: credsText !== null },
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const EXPIRED_RE = /expired|ExpiredToken|InvalidClientTokenId|UnrecognizedClient|RequestExpired|SignatureDoesNotMatch|AuthFailure|Unable to locate credentials|NoCredentialProviders|refresh|sso|credential_process|custom-process/i;
|
|
189
|
+
/** The fixes to suggest for a failed check, by credential type. */
|
|
190
|
+
export function checkFixes(v) {
|
|
191
|
+
const p = v.profile && v.profile !== 'default' ? ` --profile ${v.profile}` : '';
|
|
192
|
+
const out = [];
|
|
193
|
+
if (v.credentialType === 'credential_process')
|
|
194
|
+
out.push('Sign in again with the tool your credential_process runs, then Check now again');
|
|
195
|
+
if (v.credentialType === 'sso')
|
|
196
|
+
out.push(`aws sso login${p}`);
|
|
197
|
+
if (v.credentialType === 'static-credentials-file' || v.warnings.includes(STATIC_DEFAULT_WARNING))
|
|
198
|
+
out.push('Remove or rotate the stale static keys in ~/.aws/credentials');
|
|
199
|
+
if (v.credentialType === 'none')
|
|
200
|
+
out.push(`aws configure sso${p}`);
|
|
201
|
+
out.push('prism-cli aws connect');
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
/** The default `aws` call for Check now: aws connect's call, with the region optional and a timeout. */
|
|
205
|
+
export const CHECK_NOW_TIMEOUT_MS = 30_000;
|
|
206
|
+
export function checkNowAws(args, profile, region) {
|
|
207
|
+
return aws(region ? [...args, '--region', region] : args, { profile: profile || undefined, env: { AWS_PAGER: '' }, timeoutMs: CHECK_NOW_TIMEOUT_MS });
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* One read-only `sts get-caller-identity`, through aws connect's callerIdentity.
|
|
211
|
+
* On a match with the saved connection (same profile and region), the saved
|
|
212
|
+
* file's verifiedAt is refreshed; nothing else is written.
|
|
213
|
+
*/
|
|
214
|
+
export function checkNow(d) {
|
|
215
|
+
const v = d.identity;
|
|
216
|
+
// --profile only for a saved aws connect profile; env and the default chain are left to the AWS CLI, as every AWS command does.
|
|
217
|
+
const profile = d.saved && v.saved?.inUse ? d.saved.profile : '';
|
|
218
|
+
const region = v.region?.value ?? '';
|
|
219
|
+
const id = callerIdentity({ aws: d.aws }, profile, region);
|
|
220
|
+
if ('error' in id) {
|
|
221
|
+
const error = d.redact(id.error);
|
|
222
|
+
return { ok: false, expired: EXPIRED_RE.test(error), error, profile: v.profile, region: region || null, fixes: checkFixes(v) };
|
|
223
|
+
}
|
|
224
|
+
const s = d.saved;
|
|
225
|
+
const matches = s ? s.account === id.account : null;
|
|
226
|
+
let updated = false, note;
|
|
227
|
+
if (!s)
|
|
228
|
+
note = 'No saved aws connect connection; nothing was written. Save one with prism-cli aws connect.';
|
|
229
|
+
else if (!matches)
|
|
230
|
+
note = `Does not match the saved account ${s.account}. The saved file was not changed; check the profile, then re-run prism-cli aws connect${s.scope === 'repo' ? ' --repo' : ''}.`;
|
|
231
|
+
else if (s.profile !== v.profile || s.region !== region)
|
|
232
|
+
note = `Matches the saved account, but the check used ${v.profile ? `profile ${v.profile}` : v.source} in ${region || 'no region'}, not the saved pair; the saved file was not changed.`;
|
|
233
|
+
else {
|
|
234
|
+
(d.write ?? writeConnectionFile)(s.path, { profile: s.profile, region: s.region, account: s.account, alias: s.alias, verifiedAt: d.now().toISOString() });
|
|
235
|
+
updated = true;
|
|
236
|
+
note = `Matches the saved account; updated the verified time in ${v.saved?.path ?? s.path}.`;
|
|
237
|
+
}
|
|
238
|
+
return { ok: true, account: id.account, arn: id.arn, region: region || null, profile: v.profile, savedAccount: s?.account ?? null, matches, updated, note };
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=aws-identity.js.map
|