@prism-d1/cli 1.6.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.
Files changed (43) hide show
  1. package/dist/src/aws/connect.d.ts +1 -1
  2. package/dist/src/aws/connect.d.ts.map +1 -1
  3. package/dist/src/aws/connect.js.map +1 -1
  4. package/dist/src/commands/_top/dashboard.d.ts.map +1 -1
  5. package/dist/src/commands/_top/dashboard.js +22 -3
  6. package/dist/src/commands/_top/dashboard.js.map +1 -1
  7. package/dist/src/dashboard/actions.d.ts +112 -0
  8. package/dist/src/dashboard/actions.d.ts.map +1 -0
  9. package/dist/src/dashboard/actions.js +242 -0
  10. package/dist/src/dashboard/actions.js.map +1 -0
  11. package/dist/src/dashboard/aws-identity.d.ts +115 -0
  12. package/dist/src/dashboard/aws-identity.d.ts.map +1 -0
  13. package/dist/src/dashboard/aws-identity.js +240 -0
  14. package/dist/src/dashboard/aws-identity.js.map +1 -0
  15. package/dist/src/dashboard/page.d.ts +1 -1
  16. package/dist/src/dashboard/page.d.ts.map +1 -1
  17. package/dist/src/dashboard/page.js +23 -7
  18. package/dist/src/dashboard/page.js.map +1 -1
  19. package/dist/src/dashboard/server.d.ts +27 -6
  20. package/dist/src/dashboard/server.d.ts.map +1 -1
  21. package/dist/src/dashboard/server.js +75 -10
  22. package/dist/src/dashboard/server.js.map +1 -1
  23. package/dist/src/dashboard/setup-api.d.ts +63 -0
  24. package/dist/src/dashboard/setup-api.d.ts.map +1 -0
  25. package/dist/src/dashboard/setup-api.js +66 -0
  26. package/dist/src/dashboard/setup-api.js.map +1 -0
  27. package/dist/src/dashboard/setup-page.d.ts +16 -0
  28. package/dist/src/dashboard/setup-page.d.ts.map +1 -0
  29. package/dist/src/dashboard/setup-page.js +347 -0
  30. package/dist/src/dashboard/setup-page.js.map +1 -0
  31. package/dist/src/dashboard/setup.d.ts +2 -0
  32. package/dist/src/dashboard/setup.d.ts.map +1 -1
  33. package/dist/src/dashboard/setup.js +1 -1
  34. package/dist/src/dashboard/setup.js.map +1 -1
  35. package/dist/src/utils/aws.d.ts +2 -0
  36. package/dist/src/utils/aws.d.ts.map +1 -1
  37. package/dist/src/utils/aws.js +1 -1
  38. package/dist/src/utils/aws.js.map +1 -1
  39. package/dist/src/utils/exec.d.ts +1 -1
  40. package/dist/src/utils/exec.d.ts.map +1 -1
  41. package/dist/src/utils/exec.js +3 -1
  42. package/dist/src/utils/exec.js.map +1 -1
  43. package/package.json +1 -1
@@ -8,7 +8,11 @@
8
8
  * cookie on every request (compared in constant time);
9
9
  * - the Host header must be 127.0.0.1:<port> or localhost:<port>, which
10
10
  * defeats DNS rebinding (a rebound name arrives with its own Host);
11
- * - GET only, read-only routes, no CORS headers, strict CSP.
11
+ * - GET for every read route; no CORS headers; strict CSP.
12
+ * - POST only for the Setup page's action routes (postApi), and each POST
13
+ * also needs: an Origin (or, without one, a Referer) of this server; the
14
+ * per-run anti-CSRF token embedded in the page, in the X-Prism-CSRF header;
15
+ * and Content-Type application/json with a body of at most 16 KB.
12
16
  *
13
17
  * Why a minted token and not a platform auth mechanism: the server is
14
18
  * reachable only from this machine, the audience is the developer running it,
@@ -64,6 +68,39 @@ function send(res, status, type, body, extra = {}, nonce) {
64
68
  }
65
69
  /** /report/<id> or /report/<id>.md; anything else (slashes, dots, %-escapes) does not match. */
66
70
  const REPORT_ROUTE = /^\/report\/([A-Za-z0-9_-]{1,120})(\.md)?$/;
71
+ export const CSRF_HEADER = 'x-prism-csrf';
72
+ export const MAX_BODY = 16 * 1024;
73
+ const own = (o, k) => o && Object.prototype.hasOwnProperty.call(o, k) ? o[k] : undefined;
74
+ function readBody(req) {
75
+ return new Promise((resolve, reject) => {
76
+ let size = 0;
77
+ const chunks = [];
78
+ const onData = (c) => {
79
+ size += c.length;
80
+ // Too big: stop buffering and answer 413 (sent with Connection: close); the rest is drained, not kept.
81
+ if (size > MAX_BODY) {
82
+ req.off('data', onData);
83
+ req.resume();
84
+ resolve(null);
85
+ return;
86
+ }
87
+ chunks.push(c);
88
+ };
89
+ req.on('data', onData);
90
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
91
+ req.on('error', reject);
92
+ });
93
+ }
94
+ /** The request's Origin, else its Referer, must be this server (either Host spelling). */
95
+ function sameOrigin(req, port) {
96
+ const allowed = [`http://${BIND_HOST}:${port}`, `http://localhost:${port}`];
97
+ const origin = req.headers.origin;
98
+ if (typeof origin === 'string')
99
+ return allowed.includes(origin);
100
+ const ref = req.headers.referer;
101
+ return typeof ref === 'string' && allowed.some((a) => ref === a || ref.startsWith(a + '/'));
102
+ }
103
+ const json = (res, status, body) => send(res, status, 'application/json; charset=utf-8', JSON.stringify(body));
67
104
  export function createHandler(opts) {
68
105
  return async (req, res) => {
69
106
  try {
@@ -72,16 +109,17 @@ export function createHandler(opts) {
72
109
  if (host !== `${BIND_HOST}:${port}` && host !== `localhost:${port}`) {
73
110
  return send(res, 403, 'text/plain; charset=utf-8', 'Forbidden: unexpected Host header\n');
74
111
  }
75
- if (req.method !== 'GET') {
76
- return send(res, 405, 'text/plain; charset=utf-8', 'Method Not Allowed\n', { Allow: 'GET' });
77
- }
78
112
  const url = new URL(req.url || '/', `http://${BIND_HOST}:${port}`);
113
+ const post = own(opts.postApi, url.pathname);
114
+ if (post ? req.method !== 'POST' : req.method !== 'GET') {
115
+ return send(res, 405, 'text/plain; charset=utf-8', 'Method Not Allowed\n', { Allow: post ? 'POST' : 'GET' });
116
+ }
79
117
  const name = cookieName(port);
80
118
  const cookie = readCookie(req, name);
81
119
  const cookieOk = cookie !== null && safeEqual(cookie, opts.token);
82
120
  // First load: exchange the URL token for the cookie, then drop it from the URL.
83
121
  const qToken = url.searchParams.get('token');
84
- if (url.pathname === '/' && qToken !== null) {
122
+ if (!post && url.pathname === '/' && qToken !== null) {
85
123
  if (!safeEqual(qToken, opts.token))
86
124
  return send(res, 401, 'text/plain; charset=utf-8', 'Unauthorized\n');
87
125
  return send(res, 303, 'text/plain; charset=utf-8', 'See Other\n', {
@@ -92,14 +130,39 @@ export function createHandler(opts) {
92
130
  if (!cookieOk) {
93
131
  return send(res, 401, 'text/plain; charset=utf-8', 'Unauthorized: open the URL printed by prism-cli dashboard\n');
94
132
  }
133
+ if (post) {
134
+ if (!sameOrigin(req, port))
135
+ return json(res, 403, { error: 'Forbidden: missing or foreign Origin' });
136
+ const csrf = req.headers[CSRF_HEADER];
137
+ if (!opts.csrf || typeof csrf !== 'string' || !safeEqual(csrf, opts.csrf))
138
+ return json(res, 403, { error: 'Forbidden: missing or wrong CSRF token' });
139
+ const type = String(req.headers['content-type'] || '').split(';')[0].trim().toLowerCase();
140
+ if (type !== 'application/json')
141
+ return json(res, 415, { error: 'Content-Type must be application/json' });
142
+ const raw = await readBody(req);
143
+ if (raw === null)
144
+ return send(res, 413, 'application/json; charset=utf-8', JSON.stringify({ error: 'request body too large' }), { Connection: 'close' });
145
+ let body;
146
+ try {
147
+ body = JSON.parse(raw);
148
+ }
149
+ catch {
150
+ return json(res, 400, { error: 'invalid JSON' });
151
+ }
152
+ const r = await post(body);
153
+ return json(res, r.status, r.json);
154
+ }
95
155
  if (url.pathname === '/') {
96
156
  const nonce = randomBytes(16).toString('base64');
97
- return send(res, 200, 'text/html; charset=utf-8', renderPage(nonce), {}, nonce);
157
+ return send(res, 200, 'text/html; charset=utf-8', renderPage(nonce, opts.csrf), {}, nonce);
98
158
  }
99
159
  if (url.pathname === '/api/data') {
100
160
  const payload = await opts.getPayload();
101
161
  return send(res, 200, 'application/json; charset=utf-8', JSON.stringify(payload));
102
162
  }
163
+ const extra = own(opts.getApi, url.pathname);
164
+ if (extra)
165
+ return json(res, 200, await extra());
103
166
  const m = REPORT_ROUTE.exec(url.pathname);
104
167
  if (m && opts.getReport) {
105
168
  const report = await opts.getReport(m[1]);
@@ -127,8 +190,9 @@ export function createHandler(opts) {
127
190
  /** Starts the server on 127.0.0.1. `port` 0 (default) picks a free port. */
128
191
  export function startServer(opts) {
129
192
  const token = opts.token || newToken();
193
+ const csrf = newToken();
130
194
  let port = 0;
131
- const server = createServer(createHandler({ token, getPort: () => port, getPayload: opts.getPayload, getReport: opts.getReport }));
195
+ const server = createServer(createHandler({ token, csrf, getPort: () => port, getPayload: opts.getPayload, getReport: opts.getReport, getApi: opts.getApi, postApi: opts.postApi }));
132
196
  return new Promise((resolve, reject) => {
133
197
  server.once('error', reject);
134
198
  server.listen(opts.port ?? 0, BIND_HOST, () => {
@@ -139,19 +203,19 @@ export function startServer(opts) {
139
203
  }
140
204
  port = addr.port;
141
205
  resolve({
142
- server, port, token,
206
+ server, port, token, csrf,
143
207
  url: `http://${BIND_HOST}:${port}/?token=${token}`,
144
208
  close: () => new Promise((r) => server.close(() => r())),
145
209
  });
146
210
  });
147
211
  });
148
212
  }
149
- /** Memoises the payload for `ttlMs` and shares one in-flight build between requests. */
213
+ /** Memoises the payload for `ttlMs` and shares one in-flight build between requests. `invalidate()` forces the next build. */
150
214
  export function cachedPayload(build, ttlMs = 30_000) {
151
215
  let at = 0;
152
216
  let pending = null;
153
217
  let value;
154
- return () => {
218
+ const get = () => {
155
219
  if (value !== undefined && Date.now() - at < ttlMs)
156
220
  return Promise.resolve(value);
157
221
  if (!pending) {
@@ -159,5 +223,6 @@ export function cachedPayload(build, ttlMs = 30_000) {
159
223
  }
160
224
  return pending;
161
225
  };
226
+ return Object.assign(get, { invalidate: () => { at = 0; } });
162
227
  }
163
228
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { type ActionId, type ActionRunner, type ApiResult } from './actions.js';
2
+ import { type CheckNowDeps, type IdentityDeps } from './aws-identity.js';
3
+ /** Commands the page shows as Copy only: each writes to AWS, needs a terminal, or both. */
4
+ export declare const COPY_ONLY: {
5
+ enableSecurity: string;
6
+ addDeveloper: string;
7
+ codeReview: string;
8
+ threatModel: string;
9
+ pentest: string;
10
+ awsConnect: string;
11
+ };
12
+ export interface SetupApiDeps {
13
+ runner: ActionRunner;
14
+ repoPaths(): Promise<string[]>;
15
+ identity: IdentityDeps;
16
+ /** Where repo-less actions run, for the confirm dialog. */
17
+ cwd: string;
18
+ refresh(): Promise<unknown>;
19
+ checkAws?: CheckNowDeps['aws'];
20
+ now?: () => Date;
21
+ }
22
+ export declare function setupApi(d: SetupApiDeps): {
23
+ getApi: {
24
+ '/api/setup': () => Promise<{
25
+ actions: {
26
+ id: ActionId;
27
+ label: string;
28
+ command: string;
29
+ repo: boolean;
30
+ url: boolean;
31
+ confirm: boolean;
32
+ readOnlyAws: boolean;
33
+ timeoutSec: number;
34
+ available: boolean;
35
+ }[];
36
+ repoIds: {
37
+ [k: string]: string;
38
+ };
39
+ /** `cd <repo>` prefixes for the copy-only per-repo commands. */
40
+ repoCd: {
41
+ [k: string]: string;
42
+ };
43
+ cwd: string;
44
+ copy: {
45
+ gitleaks: string;
46
+ enableSecurity: string;
47
+ addDeveloper: string;
48
+ codeReview: string;
49
+ threatModel: string;
50
+ pentest: string;
51
+ awsConnect: string;
52
+ };
53
+ aws: import("./aws-identity.js").AwsIdentityView;
54
+ }>;
55
+ '/api/action/status': () => Promise<import("./actions.js").ActionStatus>;
56
+ '/api/recheck': () => Promise<unknown>;
57
+ };
58
+ postApi: {
59
+ '/api/action': (body: unknown) => Promise<ApiResult>;
60
+ '/api/check-now': (body: unknown) => Promise<ApiResult>;
61
+ };
62
+ };
63
+ //# sourceMappingURL=setup-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-api.d.ts","sourceRoot":"","sources":["../../../src/dashboard/setup-api.ts"],"names":[],"mappings":"AAUA,OAAO,EAA2C,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACzH,OAAO,EAA6C,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIpH,2FAA2F;AAC3F,eAAO,MAAM,SAAS;;;;;;;CAOrB,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,QAAQ,EAAE,YAAY,CAAC;IACvB,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,YAAY;;;;;;;;;;;;;;;;;YAe9B,gEAAgE;;;;;;;;;;;;;;;;;;;;8BAW9C,OAAO,KAAG,OAAO,CAAC,SAAS,CAAC;iCACzB,OAAO,KAAG,OAAO,CAAC,SAAS,CAAC;;EAc1D"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Routes behind the Setup page:
3
+ * GET /api/setup action catalog, repo ids, copy-only commands, AWS identity (no AWS call)
4
+ * GET /api/action/status the current or last action and its redacted output
5
+ * GET /api/recheck rebuilds the dashboard data now (status checks)
6
+ * POST /api/action start an allowlisted action
7
+ * POST /api/check-now one read-only sts get-caller-identity
8
+ * Account ids and ARNs appear only here and in the page, never in `dashboard --json`.
9
+ */
10
+ import { gitleaksInstallHint, shellPath } from './setup.js';
11
+ import { ACTIONS, displayCommand, redact, repoId } from './actions.js';
12
+ import { checkNow, checkNowAws, resolveAwsIdentity } from './aws-identity.js';
13
+ import { loadConnection } from '../aws/connection.js';
14
+ import { tildeHome } from './payload.js';
15
+ /** Commands the page shows as Copy only: each writes to AWS, needs a terminal, or both. */
16
+ export const COPY_ONLY = {
17
+ enableSecurity: 'prism-cli enable security',
18
+ addDeveloper: 'prism-cli team add-developer <email>',
19
+ codeReview: 'prism-cli security code-review',
20
+ threatModel: 'prism-cli security threat-model',
21
+ pentest: 'prism-cli security pentest',
22
+ awsConnect: 'prism-cli aws connect',
23
+ };
24
+ export function setupApi(d) {
25
+ const catalog = () => Object.keys(ACTIONS).map((id) => {
26
+ const a = ACTIONS[id];
27
+ return {
28
+ id, label: a.label, command: displayCommand(a.args), repo: !!a.repo, url: !!a.url, confirm: !!a.confirm,
29
+ readOnlyAws: !!a.readOnlyAws, timeoutSec: a.timeoutMs / 1000, available: d.runner.available(id),
30
+ };
31
+ });
32
+ return {
33
+ getApi: {
34
+ '/api/setup': async () => {
35
+ const paths = await d.repoPaths();
36
+ return {
37
+ actions: catalog(),
38
+ repoIds: Object.fromEntries(paths.map((p) => [p, repoId(p)])),
39
+ /** `cd <repo>` prefixes for the copy-only per-repo commands. */
40
+ repoCd: Object.fromEntries(paths.map((p) => [p, `cd ${shellPath(p)}`])),
41
+ cwd: tildeHome(d.cwd, d.identity.home),
42
+ copy: { ...COPY_ONLY, gitleaks: gitleaksInstallHint() },
43
+ aws: resolveAwsIdentity(d.identity),
44
+ };
45
+ },
46
+ '/api/action/status': async () => d.runner.status(),
47
+ '/api/recheck': async () => d.refresh(),
48
+ },
49
+ postApi: {
50
+ '/api/action': (body) => d.runner.start(body),
51
+ '/api/check-now': (body) => {
52
+ if (!body || typeof body !== 'object' || Array.isArray(body) || Object.keys(body).length) {
53
+ return Promise.resolve({ status: 400, json: { error: 'check-now takes an empty JSON object' } });
54
+ }
55
+ return d.runner.exclusive(() => checkNow({
56
+ identity: resolveAwsIdentity(d.identity),
57
+ saved: loadConnection(d.identity.home, d.identity.repoRoot),
58
+ aws: d.checkAws ?? checkNowAws,
59
+ now: d.now ?? (() => new Date()),
60
+ redact,
61
+ }));
62
+ },
63
+ },
64
+ };
65
+ }
66
+ //# sourceMappingURL=setup-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-api.js","sourceRoot":"","sources":["../../../src/dashboard/setup-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The Setup view and the theme toggle, as CSS and JS strings concatenated into
3
+ * page.ts's single document (same nonce, same el()/txt()/pill() helpers).
4
+ *
5
+ * Every value from the server is placed with textContent or an attribute.
6
+ * Run buttons only send an action id (plus a repo id or the collector URL);
7
+ * the server maps the id to a fixed argv. Each Run shows the exact command in
8
+ * a confirm dialog first. AWS-writing commands are Copy only.
9
+ */
10
+ /** Runs in <head> before first paint: applies a saved Light/Dark choice so the page does not flash. */
11
+ export declare const THEME_BOOT = "(function(){try{var t=localStorage.getItem('prism-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t);}catch(e){}})();";
12
+ /** The header toggle: Light, Dark or System (no data-theme; follows prefers-color-scheme). Saved in localStorage only. */
13
+ export declare const THEME_SCRIPT = "\nconst THEME_KEY = 'prism-theme';\nfunction storedTheme() { try { const t = localStorage.getItem(THEME_KEY); return t === 'light' || t === 'dark' ? t : 'system'; } catch (e) { return 'system'; } }\nfunction applyTheme(t) {\n const root = document.documentElement;\n if (t === 'light' || t === 'dark') root.setAttribute('data-theme', t); else root.removeAttribute('data-theme');\n for (const b of document.querySelectorAll('[data-theme-set]')) b.setAttribute('aria-pressed', String(b.getAttribute('data-theme-set') === t));\n}\nfunction setTheme(t) {\n const v = t === 'light' || t === 'dark' ? t : 'system';\n try { if (v === 'system') localStorage.removeItem(THEME_KEY); else localStorage.setItem(THEME_KEY, v); } catch (e) { /* storage off: applies for this page only */ }\n applyTheme(v);\n}\nfor (const b of document.querySelectorAll('[data-theme-set]')) b.addEventListener('click', () => setTheme(b.getAttribute('data-theme-set')));\napplyTheme(storedTheme());\n";
14
+ export declare const SETUP_STYLE = "\n[hidden] { display:none !important; }\nnav.tabs { display:flex; gap:4px; }\nnav.tabs a { color:var(--muted); text-decoration:none; padding:4px 10px; border-radius:6px; border:1px solid transparent; }\nnav.tabs a[aria-current=\"page\"] { color:var(--text); background:var(--bg); border-color:var(--line); font-weight:600; }\na:focus-visible, button:focus-visible, input:focus-visible, summary:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }\n.theme { display:inline-flex; border:1px solid var(--line); border-radius:6px; overflow:hidden; }\n.theme button { border:none; border-radius:0; background:var(--card); color:var(--muted); padding:4px 9px; }\n.theme button[aria-pressed=\"true\"] { background:var(--bg); color:var(--text); font-weight:600; }\n.srow { padding:8px 0; border-top:1px solid var(--line); overflow-wrap:anywhere; } .srow:first-of-type { border-top:none; }\n.srow-head { display:flex; gap:8px; align-items:baseline; flex-wrap:wrap; }\n.srow-ctl { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-top:6px; }\n.cmdline { display:inline-flex; gap:6px; align-items:center; flex-wrap:wrap; max-width:100%; } .cmdline code { white-space:normal; overflow-wrap:anywhere; }\n.badge { font-size:11px; padding:0 6px; border-radius:8px; border:1px solid var(--line); color:var(--muted); background:var(--raised); white-space:nowrap; }\nbutton.run { background:var(--accent); border-color:var(--accent); color:var(--card); font-weight:600; }\nbutton.run.danger { background:var(--card); border-color:var(--bad); color:var(--bad); }\nbutton:disabled { opacity:.55; cursor:not-allowed; }\ninput.url { font:inherit; padding:4px 8px; border:1px solid var(--line); border-radius:6px; background:var(--bg); color:var(--text); min-width:min(320px,100%); flex:1; }\ninput.url::placeholder { color:var(--muted); }\n.msg { color:var(--bad); font-size:12px; }\nol.steps { margin:4px 0; padding-left:20px; } ol.steps li { padding:3px 0; overflow-wrap:anywhere; }\ndl.kv { display:grid; grid-template-columns:8em minmax(0,1fr); gap:2px 10px; font-size:13px; margin:6px 0; overflow-wrap:anywhere; } dl.kv dt { color:var(--muted); } dl.kv dd { margin:0; }\ndetails.copies > summary { cursor:pointer; color:var(--muted); font-size:12px; padding:4px 0; } details.copies > div { padding:3px 0; }\ndialog.confirm { background:var(--card); color:var(--text); border:1px solid var(--line); border-radius:10px; padding:16px 18px; width:min(640px, 92vw); box-shadow:0 10px 30px rgb(0 0 0 / .35); }\ndialog.confirm::backdrop { background:rgb(0 0 0 / .5); }\ndialog.confirm h2 { margin-bottom:6px; } dialog.confirm p { margin:6px 0; }\ndialog.confirm .actions { display:flex; gap:8px; justify-content:flex-end; margin-top:14px; }\npre.cmd, pre.out { font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:12px; background:var(--bg); color:var(--text); border:1px solid var(--line); border-radius:6px; padding:8px 10px; white-space:pre-wrap; overflow-wrap:anywhere; margin:6px 0; }\npre.out { max-height:420px; overflow:auto; }\nlabel.extra { display:flex; gap:6px; align-items:flex-start; margin-top:10px; color:var(--bad); font-weight:600; }\n";
15
+ export declare const SETUP_SCRIPT = "\nconst CSRF = (document.querySelector('meta[name=\"prism-csrf\"]') || {}).content || '';\nlet SETUP = null, POLL = null, DIALOG = null, CHECK = null, URL_DRAFT = '';\nconst OUT = { status: el('div', { class: 'srow-head' }), pre: el('pre', { class: 'out', 'aria-live': 'polite' }) };\nconst ACTION_PANEL = el('section', { class: 'card wide', hidden: '' }, el('h2', {}, 'Action output'), OUT.status, OUT.pre,\n el('p', { class: 'note' }, 'Secrets are masked. Each run is logged to ~/.prism/logs/dashboard-actions.log (time, action, repo, exit code).'));\n\nfunction currentView() { return location.hash === '#setup' ? 'setup' : 'dashboard'; }\nfunction showView() {\n const v = currentView();\n document.getElementById('main').hidden = v !== 'dashboard';\n document.getElementById('setup').hidden = v !== 'setup';\n for (const a of document.querySelectorAll('[data-view]')) {\n if (a.getAttribute('data-view') === v) a.setAttribute('aria-current', 'page'); else a.removeAttribute('aria-current');\n }\n}\nwindow.addEventListener('hashchange', showView);\nshowView();\n\n/** GET, or POST JSON with the CSRF header. referrerPolicy same-origin so the browser sends this page's Origin. */\nasync function api(path, body) {\n const init = body === undefined ? { credentials: 'same-origin', cache: 'no-store' } : {\n method: 'POST', credentials: 'same-origin', cache: 'no-store', referrerPolicy: 'same-origin',\n headers: { 'Content-Type': 'application/json', 'X-Prism-CSRF': CSRF }, body: JSON.stringify(body),\n };\n const res = await fetch(path, init);\n let json = null;\n try { json = await res.json(); } catch (e) { json = null; }\n return { ok: res.ok, status: res.status, json: json || { error: 'HTTP ' + res.status } };\n}\n\nfunction ago(iso) {\n const t = Date.parse(iso);\n if (isNaN(t)) return 'unknown';\n const s = Math.max(0, (Date.now() - t) / 1000);\n for (const [n, w] of [[86400, 'day'], [3600, 'hour'], [60, 'minute']]) {\n if (s >= n) { const k = Math.floor(s / n); return k + ' ' + w + (k === 1 ? '' : 's') + ' ago'; }\n }\n return 'just now';\n}\n\nfunction copyButton(cmd) {\n const b = el('button', { type: 'button', title: 'Copy to the clipboard' }, 'Copy');\n b.addEventListener('click', async () => {\n try { await navigator.clipboard.writeText(cmd); b.textContent = 'Copied'; } catch (e) { b.textContent = 'Select the text to copy'; }\n setTimeout(() => { b.textContent = 'Copy'; }, 1500);\n });\n return el('span', { class: 'cmdline' }, el('code', {}, cmd), b);\n}\nconst copyOnly = (cmd, why) => el('div', { class: 'srow-ctl' }, copyButton(cmd), why ? el('span', { class: 'badge' }, why) : null);\n\n/** Client-side copy of the server's collector URL rule; the server checks again. */\nfunction urlProblem(s) {\n if (!s) return 'enter the collector URL';\n if (s.length > 2048) return 'the URL is longer than 2048 characters';\n let u;\n try { u = new URL(s); } catch (e) { return 'not a valid URL'; }\n if (u.protocol !== 'https:') return 'the URL must use https';\n if (!u.hostname) return 'the URL has no hostname';\n if (u.username || u.password) return 'the URL must not contain a user name or password';\n return null;\n}\n\nconst actionDef = (id) => SETUP && (SETUP.actions || []).find((a) => a.id === id);\n\n/** A Run button for an allowlisted action; null when the action is not available here. */\nfunction runButton(id, o) {\n const a = actionDef(id);\n if (!a || !a.available) return null;\n o = o || {};\n const b = el('button', { type: 'button', class: 'run' + (a.confirm ? ' danger' : '') }, o.label || 'Run');\n b.addEventListener('click', () => {\n let url;\n if (a.url) {\n url = o.urlInput ? o.urlInput.value : '';\n const p = urlProblem(url);\n if (p) { if (o.msg) o.msg.textContent = p; return; }\n if (o.msg) o.msg.textContent = '';\n }\n confirmRun(a, { repo: o.repo, where: o.where, url });\n });\n return b;\n}\n\nfunction confirmRun(a, o) {\n if (!DIALOG) { DIALOG = el('dialog', { class: 'confirm', 'aria-labelledby': 'confirm-title' }); document.body.append(DIALOG); }\n const cmd = a.command + (o.url ? ' ' + o.url : '');\n const run = el('button', { type: 'button', class: 'run' + (a.confirm ? ' danger' : '') }, 'Run');\n const cancel = el('button', { type: 'button' }, 'Cancel');\n const kids = [el('h2', { id: 'confirm-title' }, a.label), el('p', {}, 'This will run:'), el('pre', { class: 'cmd' }, cmd),\n el('p', { class: 'fix' }, 'in ' + txt(o.where || (SETUP && SETUP.cwd) || '')),\n el('p', { class: 'note' }, a.readOnlyAws ? 'Read-only in AWS: describe, get and list calls only. Nothing in AWS is changed.' : 'Changes setup on this machine only. No AWS calls.'),\n el('p', { class: 'note' }, 'It runs without a terminal, so it cannot ask questions; it is stopped after ' + Math.max(1, Math.round(a.timeoutSec / 60)) + ' min.')];\n if (a.confirm) {\n const cb = el('input', { type: 'checkbox' });\n run.disabled = true;\n cb.addEventListener('change', () => { run.disabled = !cb.checked; });\n kids.push(el('label', { class: 'extra' }, cb, 'Yes, remove the scheduled codeburn sync push from this machine'));\n }\n kids.push(el('div', { class: 'actions' }, cancel, run));\n DIALOG.replaceChildren(...kids);\n cancel.addEventListener('click', () => DIALOG.close());\n run.addEventListener('click', () => { DIALOG.close(); startAction(a, o); });\n DIALOG.showModal();\n cancel.focus();\n}\n\nasync function startAction(a, o) {\n const body = { action: a.id };\n if (a.repo) body.repo = o.repo;\n if (a.url) body.url = o.url;\n if (a.confirm) body.confirm = true;\n ACTION_PANEL.hidden = false;\n OUT.status.replaceChildren(pill('warn', 'starting'), el('code', {}, a.command));\n OUT.pre.textContent = '';\n let r;\n try { r = await api('/api/action', body); } catch (e) { r = { ok: false, json: { error: e.message } }; }\n if (!r.ok) { OUT.status.replaceChildren(pill('error', 'refused'), txt(r.json.error)); return; }\n showStatus(r.json);\n ACTION_PANEL.scrollIntoView({ block: 'nearest' });\n poll();\n}\n\nfunction showStatus(s) {\n if (!s || s.state === 'idle') return;\n ACTION_PANEL.hidden = false;\n const p = s.state === 'running' ? pill('warn', 'running') : s.exitCode === 0 ? pill('ok', 'exit 0') : pill('error', s.timedOut ? 'timed out' : 'exit ' + s.exitCode);\n OUT.status.replaceChildren(p, el('code', {}, txt(s.command)), el('span', { class: 'fix' }, (s.repo ? 'in ' + txt(s.repo) + ' \u00B7 ' : '') + 'started ' + when(s.startedAt)));\n OUT.pre.textContent = txt(s.output) || (s.state === 'running' ? '(no output yet)' : '(no output)');\n OUT.pre.scrollTop = OUT.pre.scrollHeight;\n}\n\nfunction poll() {\n clearTimeout(POLL);\n POLL = setTimeout(async () => {\n try {\n const r = await api('/api/action/status');\n if (r.ok) showStatus(r.json);\n if (r.ok && r.json.state !== 'running') { load(); return; }\n } catch (e) { /* retry */ }\n poll();\n }, 1000);\n}\n\nfunction srow(status, label, detail, ...ctl) {\n const c = ctl.filter(Boolean);\n return el('div', { class: 'srow' }, el('div', { class: 'srow-head' }, status, el('b', {}, label)),\n detail ? el('div', { class: 'fix' }, detail) : null, c.length ? el('div', { class: 'srow-ctl' }, c) : null);\n}\n\nfunction machineSection(data) {\n const s = el('section', { class: 'card' }, el('h2', {}, 'This machine'));\n for (const i of data.machine.setup) {\n if (i.id === 'codeburn-sync' || i.id === 'aws-credentials') continue;\n let ctl = null;\n if (i.id === 'codewatch-global' && i.status !== 'ok') {\n const elsewhere = (data.machine.globalHooks || {}).state === 'elsewhere';\n ctl = elsewhere ? copyOnly('prism-cli enable codewatch --global --force', 'replaces another global hooks path')\n : runButton('codewatch-global', { label: 'Enable for every repo' });\n } else if (i.fix) {\n ctl = copyOnly(i.fix, i.id === 'gitleaks' ? 'installs software' : i.id === 'codeburn' ? 'asks before installing' : null);\n }\n s.append(srow(pill(i.status), i.label, i.detail, ctl));\n }\n s.append(el('p', { class: 'note' }, 'AWS credentials: see the AWS section.'));\n return s;\n}\n\nconst CW_PILL = { ok: 'ok', warn: 'warn', missing: 'missing', 'opted-out': 'skipped' };\nfunction reposSection(data) {\n const s = el('section', { class: 'card wide' }, el('h2', {}, 'Repos'));\n const recheck = el('button', { type: 'button' }, 'Re-check status');\n recheck.addEventListener('click', async () => { recheck.disabled = true; try { await api('/api/recheck'); } catch (e) { /* load() reports */ } await load(); });\n s.append(el('div', { class: 'srow-ctl' }, recheck, el('span', { class: 'fix' }, 'Codewatch hooks and Security Agent setup per repo, from local files.')));\n const ids = SETUP.repoIds || {}, cds = SETUP.repoCd || {}, cp = SETUP.copy || {};\n const rowOf = (r) => {\n const cw = el('div', {}, pill(CW_PILL[r.status] || 'missing', r.status === 'opted-out' ? 'opted out' : r.status + (r.status === 'ok' && r.global ? ' (global)' : '')), ' ',\n el('span', { class: 'fix' }, r.status === 'ok' ? 'reviewer ' + txt(r.reviewer || 'none') : txt(r.detail)));\n if ((r.status === 'missing' || r.status === 'warn') && ids[r.path]) cw.append(el('div', { class: 'srow-ctl' }, runButton('codewatch-repo', { repo: ids[r.path], where: r.path, label: 'Enable codewatch' })));\n const sec = r.security || { status: 'missing' };\n const sc = el('div', {}, pill(sec.status === 'missing' ? 'missing' : 'ok', sec.status === 'in-use' ? 'in use' : sec.status));\n const cd = cds[r.path] ? cds[r.path] + ' && ' : '';\n if (sec.status === 'missing') sc.append(copyOnly(sec.fix || cd + cp.enableSecurity, 'AWS'));\n else sc.append(el('details', { class: 'copies' }, el('summary', {}, 'Security Agent commands (AWS, copy only)'),\n [cp.codeReview, cp.threatModel, cp.pentest].map((c) => el('div', {}, copyButton(cd + c)))));\n return [repoName(r), cw, sc];\n };\n const rows = data.overview.setup || [];\n const shown = rows.filter((r) => !r.collapsed), hidden = rows.filter((r) => r.collapsed);\n if (shown.length) s.append(table(['Repo', 'Codewatch', 'Security Agent'], shown.map(rowOf)));\n else if (!hidden.length) s.append(el('p', { class: 'empty' }, 'No git repositories found under ' + txt(data.scope.root_display) + '.'));\n if (hidden.length) s.append(el('details', { class: 'missing-group', 'data-key': 'setup-view:other' },\n el('summary', {}, hidden.length + ' other repo' + (hidden.length === 1 ? '' : 's') + ': not set up'), table(['Repo', 'Codewatch', 'Security Agent'], hidden.map(rowOf))));\n return s;\n}\n\nfunction syncSection(data) {\n const s = el('section', { class: 'card' }, el('h2', {}, 'Usage sync (codeburn)'));\n const item = data.machine.setup.find((i) => i.id === 'codeburn-sync');\n const mode = (data.machine.codeburnSync || {}).mode || 'local';\n if (item) s.append(srow(pill(item.status), item.label, item.detail));\n if (item && item.help) s.append(helpBlock(item.help));\n s.append(el('p', { class: 'fix' }, 'Sync mode: ' + mode));\n s.append(srow(null, 'Local only', 'codeburn usage stays on this machine; no endpoint and no schedule.', runButton('codeburn-local', { label: 'Keep usage local' })));\n const input = el('input', { type: 'url', class: 'url', placeholder: 'collector URL (https, from your platform team)', maxlength: '2048', 'aria-label': 'Collector URL', spellcheck: 'false', autocomplete: 'off' });\n input.value = URL_DRAFT;\n input.addEventListener('input', () => { URL_DRAFT = input.value; });\n const msg = el('span', { class: 'msg', role: 'status' });\n s.append(srow(null, \"Sync to my team's PRISM\", 'Sets up codeburn sync for the collector URL and schedules the push. codeburn opens a browser to sign in.',\n input, runButton('codeburn-team', { label: 'Sync to team', urlInput: input, msg }), msg));\n if (actionDef('codeburn-systemd') && actionDef('codeburn-systemd').available) {\n s.append(srow(null, 'Use a systemd user timer', mode === 'team' ? 'Schedules the sync push with a systemd user timer; your crontab is not touched.' : 'Team mode only: set up sync to your team first.',\n mode === 'team' ? runButton('codeburn-systemd', { label: 'Use systemd timer' }) : null));\n }\n s.append(srow(null, 'Remove the sync schedule', 'Removes the scheduled sync push (cron entry or systemd timer). codeburn and its sync config stay.', runButton('codeburn-remove', { label: 'Remove schedule' })));\n s.append(srow(null, 'Status', 'codeburn, sync and schedule status.', runButton('codeburn-status', { label: 'Show status' })));\n s.append(srow(null, 'Platform team: add a developer', 'Creates a telemetry account in AWS (Cognito), so it is copy only.', copyOnly((SETUP.copy || {}).addDeveloper, 'AWS write')));\n return s;\n}\n\nconst STEP_PILL = { used: ['ok', 'used'], skipped: ['skipped', 'skipped'], 'not-set': ['info', 'not set'], 'n/a': ['skipped', 'n/a'] };\nfunction kv(pairs) { return el('dl', { class: 'kv' }, pairs.filter(Boolean).flatMap(([k, v]) => [el('dt', {}, k), el('dd', {}, v)])); }\n\nfunction checkResult(r) {\n if (!r) return null;\n if (!r.ok) return el('div', {}, pill('error', 'refused'), ' ', txt(r.json.error));\n const j = r.json;\n if (j.ok) {\n const m = j.matches === true ? pill('ok', 'matches the saved account') : j.matches === false ? pill('error', 'does not match saved account ' + txt(j.savedAccount)) : pill('info', 'no saved account');\n return el('div', {}, pill('ok', 'credentials work'), kv([['Account', txt(j.account)], ['Caller', txt(j.arn)], ['Region', txt(j.region || 'not set')], ['Saved', m]]), el('p', { class: 'note' }, txt(j.note)));\n }\n return el('div', {}, pill('error', j.expired ? 'expired or invalid' : 'check failed'), ' ', el('span', {}, txt(j.error)),\n el('div', { class: 'help' }, 'Fixes:', el('ul', { class: 'help-steps' }, (j.fixes || []).map((f) => el('li', {}, /^(aws|prism-cli) /.test(f) ? copyButton(f) : txt(f))))));\n}\n\nfunction awsSection(a) {\n const s = el('section', { class: 'card wide' }, el('h2', {}, 'AWS'));\n s.append(el('p', { class: 'note' }, 'From the environment and local config files only: no AWS call until you press Check now. Only setting names are read from ~/.aws; secret values are never read or shown.'));\n s.append(el('div', { class: 'srow' }, el('div', {}, 'prism-cli would use ', el('b', {}, a.profile ? 'profile ' + txt(a.profile) : txt(a.source)), ' \u00B7 ', el('span', { class: 'badge' }, txt(a.credentialLabel))),\n el('div', { class: 'fix' }, 'chosen by ' + txt(a.source) + ' \u00B7 region ' + (a.region ? txt(a.region.value) + ' (' + txt(a.region.source) + ')' : 'not set'))));\n for (const w of a.warnings || []) s.append(el('div', { class: 'callout warn', role: 'note' }, txt(w)));\n s.append(el('h3', {}, 'Resolution order'), el('ol', { class: 'steps' }, (a.steps || []).map((st) => {\n const p = STEP_PILL[st.state] || ['skipped', st.state];\n return el('li', {}, el('b', {}, txt(st.label)), ' ', pill(p[0], p[1]), ' ', el('span', { class: 'fix' }, txt(st.detail)));\n })));\n s.append(el('h3', {}, 'Saved aws connect'));\n if (a.saved) {\n const v = a.saved;\n s.append(kv([['Account', el('span', {}, txt(v.account), v.alias ? el('span', { class: 'tag' }, txt(v.alias)) : null)], ['Profile', txt(v.profile) + (v.inUse ? '' : ' (not in use)')],\n ['Region', txt(v.region)], ['Verified', v.verifiedAt ? el('span', { title: when(v.verifiedAt) }, ago(v.verifiedAt)) : 'unknown'], ['File', txt(v.path) + ' (' + v.scope + ')'],\n ['Admin / org', 'not stored by aws connect (it prints them when it runs)']]));\n } else s.append(el('p', { class: 'empty-line' }, 'None saved. ', copyButton((SETUP.copy || {}).awsConnect)));\n const btn = el('button', { type: 'button', class: 'run' }, 'Check now');\n const out = el('div', { 'aria-live': 'polite' }, checkResult(CHECK));\n btn.addEventListener('click', async () => {\n btn.disabled = true;\n out.replaceChildren(el('span', { class: 'fix' }, 'checking...'));\n try { CHECK = await api('/api/check-now', {}); } catch (e) { CHECK = { ok: false, json: { error: e.message } }; }\n out.replaceChildren(checkResult(CHECK));\n btn.disabled = false;\n if (CHECK.ok && CHECK.json.updated) load();\n });\n s.append(el('h3', {}, 'Check now'), el('div', { class: 'srow-ctl' }, btn, el('span', { class: 'fix' }, 'One read-only call: aws sts get-caller-identity, as aws connect does it.')), out);\n s.append(el('h3', {}, 'Read-only AWS commands'),\n srow(null, 'Account check', 'Every call is a describe, get or list; the result is saved to .prism/results/.', runButton('aws-check', { label: 'Run aws check' })),\n srow(null, 'Team stack info', 'Reads the PRISM stack outputs (collector URL, user pool).', runButton('team-info', { label: 'Run team info' })));\n const cp = SETUP.copy || {};\n s.append(el('h3', {}, 'Copy only (writes to AWS or needs a terminal)'),\n srow(null, 'Choose and save the AWS profile', 'Asks questions on a terminal.', copyOnly(cp.awsConnect, 'terminal')),\n srow(null, 'Set up the Security Agent in a repo', 'Creates AWS resources.', copyOnly(cp.enableSecurity, 'AWS write')),\n srow(null, 'Add a developer telemetry account', 'Creates a Cognito user.', copyOnly(cp.addDeveloper, 'AWS write')),\n srow(null, 'Security Agent runs', 'Uploads code and starts AWS jobs.', copyOnly(cp.codeReview, 'AWS write'), copyOnly(cp.threatModel, 'AWS write'), copyOnly(cp.pentest, 'AWS write')));\n return s;\n}\n\nasync function renderSetup(data) {\n try { const r = await api('/api/setup'); if (r.ok) SETUP = r.json; } catch (e) { /* keep the last good copy */ }\n const root = document.getElementById('setup');\n const openState = new Map([...root.querySelectorAll('details[data-key]')].map((d) => [d.getAttribute('data-key'), d.open]));\n root.replaceChildren();\n if (!SETUP) { root.append(el('section', { class: 'card wide' }, pill('error'), ' could not load the setup data')); return; }\n root.append(machineSection(data), syncSection(data), reposSection(data), awsSection(SETUP.aws || {}), ACTION_PANEL);\n for (const d of root.querySelectorAll('details[data-key]')) { const k = d.getAttribute('data-key'); if (openState.has(k)) d.open = openState.get(k); }\n}\n// A reload while an action runs picks its output back up.\napi('/api/action/status').then((r) => { if (r.ok && r.json.state === 'running') { showStatus(r.json); poll(); } }).catch(() => {});\n";
16
+ //# sourceMappingURL=setup-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/setup-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,uGAAuG;AACvG,eAAO,MAAM,UAAU,gKAAgK,CAAC;AAExL,0HAA0H;AAC1H,eAAO,MAAM,YAAY,m9BAexB,CAAC;AAEF,eAAO,MAAM,WAAW,inGA8BvB,CAAC;AAEF,eAAO,MAAM,YAAY,qpkBA8RxB,CAAC"}