sliccy 2.42.0 → 2.43.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/node-server/_shared/index.d.ts +4 -0
- package/dist/node-server/_shared/index.d.ts.map +1 -0
- package/dist/node-server/_shared/index.js +5 -0
- package/dist/node-server/_shared/index.js.map +1 -0
- package/dist/node-server/_shared/oauth-extra-domains-storage.d.ts +29 -0
- package/dist/node-server/_shared/oauth-extra-domains-storage.d.ts.map +1 -0
- package/dist/node-server/_shared/oauth-extra-domains-storage.js +85 -0
- package/dist/node-server/_shared/oauth-extra-domains-storage.js.map +1 -0
- package/dist/node-server/_shared/secret-masking.d.ts +43 -0
- package/dist/node-server/_shared/secret-masking.d.ts.map +1 -0
- package/dist/node-server/_shared/secret-masking.js +125 -0
- package/dist/node-server/_shared/secret-masking.js.map +1 -0
- package/dist/node-server/_shared/secrets-pipeline.d.ts +103 -0
- package/dist/node-server/_shared/secrets-pipeline.d.ts.map +1 -0
- package/dist/node-server/_shared/secrets-pipeline.js +249 -0
- package/dist/node-server/_shared/secrets-pipeline.js.map +1 -0
- package/dist/node-server/index.js +78 -7
- package/dist/node-server/secrets/index.d.ts +3 -3
- package/dist/node-server/secrets/index.js +1 -1
- package/dist/node-server/secrets/oauth-secret-store.d.ts +12 -0
- package/dist/node-server/secrets/oauth-secret-store.js +18 -0
- package/dist/node-server/secrets/proxy-manager.d.ts +13 -49
- package/dist/node-server/secrets/proxy-manager.js +62 -114
- package/dist/node-server/secrets/session-id-file.d.ts +5 -0
- package/dist/node-server/secrets/session-id-file.js +33 -0
- package/dist/node-server/secrets/types.d.ts +5 -0
- package/dist/ui/assets/adobe-C2UaIA41.js +2 -0
- package/dist/ui/assets/adobe-DCEW-jwu.js +1 -0
- package/dist/ui/assets/{agent-bridge-DPiRY9Cp.js → agent-bridge-DpnoOA1m.js} +1 -1
- package/dist/ui/assets/{agent-message-to-chat-DPXL2yWd.js → agent-message-to-chat-BhoxpmpE.js} +1 -1
- package/dist/ui/assets/{anthropic-DNaAx4PR.js → anthropic-BBCljDKG.js} +1 -1
- package/dist/ui/assets/{anthropic-z3OB3Fnl.js → anthropic-DuP0qmVz.js} +1 -1
- package/dist/ui/assets/{azure-openai-T9qO12vJ.js → azure-openai-DJep1H6m.js} +1 -1
- package/dist/ui/assets/{azure-openai-BittXGOl.js → azure-openai-es2UEaVa.js} +1 -1
- package/dist/ui/assets/{azure-openai-responses-DBxhuZoo.js → azure-openai-responses-BasmI9oz.js} +1 -1
- package/dist/ui/assets/{azure-openai-responses-DviTxw7q.js → azure-openai-responses-DBAbXSTD.js} +1 -1
- package/dist/ui/assets/{bedrock-camp-CHTXSY1O.js → bedrock-camp-CSp2DPSu.js} +3 -3
- package/dist/ui/assets/{bedrock-camp-MgGI9Yh2.js → bedrock-camp-KyNM28gW.js} +2 -2
- package/dist/ui/assets/{cdp-BV0AqGz6.js → cdp-CP2mm6ER.js} +2 -2
- package/dist/ui/assets/cost-command-DnyD68nK.js +1 -0
- package/dist/ui/assets/{es-CMpEjX7X.js → es-BUhsge_v.js} +1 -1
- package/dist/ui/assets/{fs-CJrk6h4U.js → fs-1U1rT6mb.js} +2 -2
- package/dist/ui/assets/{fs-IOuBZ0fU.js → fs-KzDX6lDt.js} +3 -3
- package/dist/ui/assets/github-BoI7NK5Y.js +2 -0
- package/dist/ui/assets/github-DZWbceUJ.js +1 -0
- package/dist/ui/assets/{google-B_wiPl8S.js → google-CcdeRF42.js} +1 -1
- package/dist/ui/assets/{google-BSQPUbsD.js → google-w3iuVje2.js} +1 -1
- package/dist/ui/assets/{index-DOah61oi.js → index-Dbfn2pUX.js} +17 -17
- package/dist/ui/assets/{kernel-worker-Ct6nuQEv.js → kernel-worker-BuJdZy5v.js} +920 -887
- package/dist/ui/assets/{local-llm-BKrBXM1b.js → local-llm-D72tLY3Y.js} +1 -1
- package/dist/ui/assets/{local-llm-DG_WcsYf.js → local-llm-XVLqdOOf.js} +1 -1
- package/dist/ui/assets/{mistral-cWWq2371.js → mistral-DRSbEgc2.js} +1 -1
- package/dist/ui/assets/{mistral-oA9eus1c.js → mistral-b5JBKUqy.js} +1 -1
- package/dist/ui/assets/{mount-BE4HlHj3.js → mount-6-HP-fzb.js} +1 -1
- package/dist/ui/assets/{mount-d5zFj5UF.js → mount-zcpBZ465.js} +2 -2
- package/dist/ui/assets/{nuke-command-Do01VR7B.js → nuke-command-DTu62dL0.js} +1 -1
- package/dist/ui/assets/oauth-bootstrap-CsJTQqoE.js +1 -0
- package/dist/ui/assets/oauth-service-C0rdcoXi.js +1 -0
- package/dist/ui/assets/oauth-service-DTHr_ILl.js +1 -0
- package/dist/ui/assets/{offscreen-client-D96UbGR3.js → offscreen-client-BQS5SL9t.js} +1 -1
- package/dist/ui/assets/{onboarding-orchestrator-D1l-w9_h.js → onboarding-orchestrator-Dld2OQzb.js} +1 -1
- package/dist/ui/assets/{openai-codex-responses-ChYFmtB8.js → openai-codex-responses-BIq_NjtP.js} +1 -1
- package/dist/ui/assets/{openai-codex-responses-DYtt5_H9.js → openai-codex-responses-Bw3r2Hnu.js} +1 -1
- package/dist/ui/assets/{openai-completions-CkeC09FW.js → openai-completions-68Ek_N21.js} +1 -1
- package/dist/ui/assets/{openai-completions-pJVLdfB5.js → openai-completions-BXM-gDWt.js} +1 -1
- package/dist/ui/assets/{openai-responses-CfoWjQjX.js → openai-responses-BoV1AGA6.js} +1 -1
- package/dist/ui/assets/{openai-responses-BtUhfSCM.js → openai-responses-EH-UgPTz.js} +1 -1
- package/dist/ui/assets/{openai-responses-shared-BT4PBv9i.js → openai-responses-shared-BnEuqygR.js} +1 -1
- package/dist/ui/assets/{openai-responses-shared-BL73-u8o.js → openai-responses-shared-sCOhnzkS.js} +1 -1
- package/dist/ui/assets/page-storage-sync-DEhI1xEB.js +1 -0
- package/dist/ui/assets/{panel-rpc-BLyR6B42.js → panel-rpc-g85bMVnq.js} +1 -1
- package/dist/ui/assets/panel-rpc-handlers-Cd0NeWGr.js +1 -0
- package/dist/ui/assets/provider-settings-BJW3Z-UR.js +2 -0
- package/dist/ui/assets/provider-settings-BwyVvIHC.js +90 -0
- package/dist/ui/assets/{providers-DDa7zuci.js → providers-Bx2_S0Gs.js} +1 -1
- package/dist/ui/assets/secret-env-DES2XPkD.js +1 -0
- package/dist/ui/assets/{spawn-CIR7ZcER.js → spawn-Bs7QOa4F.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-CHed1OmR.js → upgrade-detection-DaIEsDu3.js} +1 -1
- package/dist/ui/index.html +6 -6
- package/dist/ui/packages/webapp/index.html +6 -6
- package/package.json +4 -3
- package/dist/node-server/secrets/domain-match.d.ts +0 -20
- package/dist/node-server/secrets/domain-match.js +0 -37
- package/dist/node-server/secrets/masking.d.ts +0 -21
- package/dist/node-server/secrets/masking.js +0 -82
- package/dist/ui/assets/adobe-CqL9wyDB.js +0 -1
- package/dist/ui/assets/adobe-Dz3AQqOU.js +0 -1
- package/dist/ui/assets/cost-command-CmqxOe10.js +0 -1
- package/dist/ui/assets/github-B1wRLpqB.js +0 -1
- package/dist/ui/assets/github-BtI_MJWq.js +0 -2
- package/dist/ui/assets/oauth-service-BOg00ewY.js +0 -1
- package/dist/ui/assets/oauth-service-CfTUWB9P.js +0 -1
- package/dist/ui/assets/page-storage-sync-BPAEqPof.js +0 -1
- package/dist/ui/assets/panel-rpc-handlers-ZHQOP2j9.js +0 -1
- package/dist/ui/assets/provider-settings-CvNDi0PS.js +0 -90
- package/dist/ui/assets/provider-settings-DUgrWSPY.js +0 -2
- package/dist/ui/assets/secret-masking-BMzkw8ZK.js +0 -1
- /package/dist/ui/assets/{azure-ai-foundry-CaxHzIfg.js → azure-ai-foundry-BHhe6xrk.js} +0 -0
- /package/dist/ui/assets/{azure-ai-foundry-B1O-wE_V.js → azure-ai-foundry-Bcv3AvZm.js} +0 -0
- /package/dist/ui/assets/{bsh-watchdog-BPXsh13l.js → bsh-watchdog-BBjbd-XL.js} +0 -0
- /package/dist/ui/assets/{chat-fixture-CM-jsDDl.js → chat-fixture-5h_JuUYl.js} +0 -0
- /package/dist/ui/assets/{git-config-BoQzX7Xi.js → git-config-BMEHAjvp.js} +0 -0
- /package/dist/ui/assets/{magick-wasm-BnlzD3um.js → magick-wasm-E5pMp5Fh.js} +0 -0
- /package/dist/ui/assets/{remote-terminal-view-wMCUFwpP.js → remote-terminal-view-B3w1XsI8.js} +0 -0
- /package/dist/ui/assets/{rum-DYAJNvRR.js → rum-_zaGz-D7.js} +0 -0
- /package/dist/ui/assets/{slicc-editor-ZPVWM2cU.js → slicc-editor-DAgxp34c.js} +0 -0
- /package/dist/ui/assets/{sprinkle-bridge-channel-Dd_0GEWx.js → sprinkle-bridge-channel-DJuNr3kv.js} +0 -0
- /package/dist/ui/assets/{sprinkle-bridge-channel-DhKQ_srk.js → sprinkle-bridge-channel-wZyMV5ic.js} +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { mask as cryptoMask, buildScrubber, matchesDomains, } from './secret-masking.js';
|
|
2
|
+
function indexOfBytes(haystack, needle, from = 0) {
|
|
3
|
+
outer: for (let i = from; i <= haystack.length - needle.length; i++) {
|
|
4
|
+
for (let j = 0; j < needle.length; j++) {
|
|
5
|
+
if (haystack[i + j] !== needle[j])
|
|
6
|
+
continue outer;
|
|
7
|
+
}
|
|
8
|
+
return i;
|
|
9
|
+
}
|
|
10
|
+
return -1;
|
|
11
|
+
}
|
|
12
|
+
function replaceAllBytes(haystack, needle, replacement) {
|
|
13
|
+
if (indexOfBytes(haystack, needle) < 0)
|
|
14
|
+
return haystack;
|
|
15
|
+
const out = [];
|
|
16
|
+
let i = 0;
|
|
17
|
+
while (i < haystack.length) {
|
|
18
|
+
const idx = indexOfBytes(haystack, needle, i);
|
|
19
|
+
if (idx < 0) {
|
|
20
|
+
for (let k = i; k < haystack.length; k++)
|
|
21
|
+
out.push(haystack[k]);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
for (let k = i; k < idx; k++)
|
|
25
|
+
out.push(haystack[k]);
|
|
26
|
+
for (let k = 0; k < replacement.length; k++)
|
|
27
|
+
out.push(replacement[k]);
|
|
28
|
+
i = idx + needle.length;
|
|
29
|
+
}
|
|
30
|
+
return new Uint8Array(out);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Stateful unmask/scrub pipeline shared between node-server's /api/fetch-proxy
|
|
34
|
+
* and the chrome-extension SW's fetch-proxy.fetch Port handler.
|
|
35
|
+
*
|
|
36
|
+
* Public surface has four method families:
|
|
37
|
+
*
|
|
38
|
+
* ┌────────────┬────────────────────────────────┬─────────────────────────┐
|
|
39
|
+
* │ │ Text-safe (string in / out) │ Byte-safe (Uint8Array) │
|
|
40
|
+
* ├────────────┼────────────────────────────────┼─────────────────────────┤
|
|
41
|
+
* │ Unmask │ unmask, unmaskBody, │ unmaskBodyBytes │
|
|
42
|
+
* │ (mask→real)│ unmaskHeaders, …Basic, …Url │ │
|
|
43
|
+
* ├────────────┼────────────────────────────────┼─────────────────────────┤
|
|
44
|
+
* │ Scrub │ scrubResponse, scrubHeaders │ scrubResponseBytes │
|
|
45
|
+
* │ (real→mask)│ │ │
|
|
46
|
+
* └────────────┴────────────────────────────────┴─────────────────────────┘
|
|
47
|
+
*
|
|
48
|
+
* Use the byte-safe variants for request/response bodies that may be binary
|
|
49
|
+
* (git packfiles, ZIPs, images, application/octet-stream). The text variants
|
|
50
|
+
* UTF-8-decode their input, which corrupts non-UTF-8 byte sequences
|
|
51
|
+
* (`Buffer.toString('utf-8')` replaces invalid bytes with U+FFFD).
|
|
52
|
+
*
|
|
53
|
+
* Note: unmaskHeaders MUTATES its input in place (matching SecretProxyManager's
|
|
54
|
+
* legacy semantics). The other methods return new strings/byte arrays.
|
|
55
|
+
*/
|
|
56
|
+
export class SecretsPipeline {
|
|
57
|
+
sessionId;
|
|
58
|
+
source;
|
|
59
|
+
maskedToSecret = new Map();
|
|
60
|
+
scrubber = (t) => t;
|
|
61
|
+
constructor(opts) {
|
|
62
|
+
this.sessionId = opts.sessionId;
|
|
63
|
+
this.source = opts.source;
|
|
64
|
+
}
|
|
65
|
+
async reload() {
|
|
66
|
+
const all = await this.source.listAll();
|
|
67
|
+
const next = new Map();
|
|
68
|
+
for (const s of all) {
|
|
69
|
+
const maskedValue = await cryptoMask(this.sessionId, s.name, s.value);
|
|
70
|
+
next.set(maskedValue, {
|
|
71
|
+
name: s.name,
|
|
72
|
+
realValue: s.value,
|
|
73
|
+
maskedValue,
|
|
74
|
+
domains: s.domains,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
this.maskedToSecret = next;
|
|
78
|
+
const pairs = Array.from(next.values()).map((ms) => ({
|
|
79
|
+
realValue: ms.realValue,
|
|
80
|
+
maskedValue: ms.maskedValue,
|
|
81
|
+
}));
|
|
82
|
+
this.scrubber = buildScrubber(pairs);
|
|
83
|
+
}
|
|
84
|
+
async maskOne(name, value) {
|
|
85
|
+
return cryptoMask(this.sessionId, name, value);
|
|
86
|
+
}
|
|
87
|
+
hasSecrets() {
|
|
88
|
+
return this.maskedToSecret.size > 0;
|
|
89
|
+
}
|
|
90
|
+
getMaskedEntries() {
|
|
91
|
+
return Array.from(this.maskedToSecret.values()).map((ms) => ({
|
|
92
|
+
name: ms.name,
|
|
93
|
+
maskedValue: ms.maskedValue,
|
|
94
|
+
domains: ms.domains,
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Unmask a single string. Domain mismatch on a matched secret → forbidden.
|
|
99
|
+
* Returns { text } on success, { text: original, forbidden } on block.
|
|
100
|
+
*/
|
|
101
|
+
unmask(text, hostname) {
|
|
102
|
+
let result = text;
|
|
103
|
+
for (const [maskedValue, ms] of this.maskedToSecret) {
|
|
104
|
+
if (!result.includes(maskedValue))
|
|
105
|
+
continue;
|
|
106
|
+
if (!matchesDomains(hostname, ms.domains)) {
|
|
107
|
+
return { text, forbidden: { secretName: ms.name, hostname } };
|
|
108
|
+
}
|
|
109
|
+
result = result.split(maskedValue).join(ms.realValue);
|
|
110
|
+
}
|
|
111
|
+
return { text: result };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Unmask body text. Domain mismatch on a matched secret leaves it untouched
|
|
115
|
+
* (NO forbidden — masked values in conversation context are harmless).
|
|
116
|
+
*/
|
|
117
|
+
unmaskBody(text, hostname) {
|
|
118
|
+
let result = text;
|
|
119
|
+
for (const [maskedValue, ms] of this.maskedToSecret) {
|
|
120
|
+
if (!result.includes(maskedValue))
|
|
121
|
+
continue;
|
|
122
|
+
if (!matchesDomains(hostname, ms.domains))
|
|
123
|
+
continue;
|
|
124
|
+
result = result.split(maskedValue).join(ms.realValue);
|
|
125
|
+
}
|
|
126
|
+
return { text: result };
|
|
127
|
+
}
|
|
128
|
+
unmaskAuthorizationBasic(headerValue, hostname) {
|
|
129
|
+
const pattern = /^Basic\s+(.+)$/;
|
|
130
|
+
const match = pattern.exec(headerValue);
|
|
131
|
+
if (!match)
|
|
132
|
+
return { value: headerValue };
|
|
133
|
+
let decoded;
|
|
134
|
+
try {
|
|
135
|
+
decoded = atob(match[1].trim());
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return { value: headerValue };
|
|
139
|
+
}
|
|
140
|
+
const colon = decoded.indexOf(':');
|
|
141
|
+
if (colon < 0)
|
|
142
|
+
return { value: headerValue };
|
|
143
|
+
let user = decoded.slice(0, colon);
|
|
144
|
+
let pass = decoded.slice(colon + 1);
|
|
145
|
+
let touched = false;
|
|
146
|
+
for (const [maskedValue, ms] of this.maskedToSecret) {
|
|
147
|
+
if (user.includes(maskedValue) || pass.includes(maskedValue)) {
|
|
148
|
+
if (!matchesDomains(hostname, ms.domains)) {
|
|
149
|
+
return { value: headerValue, forbidden: { secretName: ms.name, hostname } };
|
|
150
|
+
}
|
|
151
|
+
if (user.includes(maskedValue))
|
|
152
|
+
user = user.split(maskedValue).join(ms.realValue);
|
|
153
|
+
if (pass.includes(maskedValue))
|
|
154
|
+
pass = pass.split(maskedValue).join(ms.realValue);
|
|
155
|
+
touched = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!touched)
|
|
159
|
+
return { value: headerValue };
|
|
160
|
+
return { value: `Basic ${btoa(`${user}:${pass}`)}` };
|
|
161
|
+
}
|
|
162
|
+
extractAndUnmaskUrlCredentials(rawUrl) {
|
|
163
|
+
let parsed;
|
|
164
|
+
try {
|
|
165
|
+
parsed = new URL(rawUrl);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return { url: rawUrl };
|
|
169
|
+
}
|
|
170
|
+
if (!parsed.username && !parsed.password)
|
|
171
|
+
return { url: rawUrl };
|
|
172
|
+
let user = decodeURIComponent(parsed.username);
|
|
173
|
+
let pass = decodeURIComponent(parsed.password);
|
|
174
|
+
const host = parsed.host;
|
|
175
|
+
let touched = false;
|
|
176
|
+
for (const [maskedValue, ms] of this.maskedToSecret) {
|
|
177
|
+
if (user.includes(maskedValue) || pass.includes(maskedValue)) {
|
|
178
|
+
if (!matchesDomains(host, ms.domains)) {
|
|
179
|
+
return { url: rawUrl, forbidden: { secretName: ms.name, hostname: host } };
|
|
180
|
+
}
|
|
181
|
+
if (user.includes(maskedValue)) {
|
|
182
|
+
user = user.split(maskedValue).join(ms.realValue);
|
|
183
|
+
touched = true;
|
|
184
|
+
}
|
|
185
|
+
if (pass.includes(maskedValue)) {
|
|
186
|
+
pass = pass.split(maskedValue).join(ms.realValue);
|
|
187
|
+
touched = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const synthetic = touched && (user || pass) ? `Basic ${btoa(`${user}:${pass}`)}` : undefined;
|
|
192
|
+
parsed.username = '';
|
|
193
|
+
parsed.password = '';
|
|
194
|
+
return { url: parsed.toString(), syntheticAuthorization: synthetic };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Unmask headers IN PLACE. Mutates the headers parameter; returns only { forbidden? }.
|
|
198
|
+
* Match SecretProxyManager's existing semantics so call sites compile unchanged.
|
|
199
|
+
*/
|
|
200
|
+
unmaskHeaders(headers, hostname) {
|
|
201
|
+
for (const [key, val] of Object.entries(headers)) {
|
|
202
|
+
if (key.toLowerCase() === 'authorization' && /^Basic\s/i.test(val)) {
|
|
203
|
+
const basic = this.unmaskAuthorizationBasic(val, hostname);
|
|
204
|
+
if (basic.forbidden)
|
|
205
|
+
return { forbidden: basic.forbidden };
|
|
206
|
+
headers[key] = basic.value;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const { text, forbidden } = this.unmask(val, hostname);
|
|
210
|
+
if (forbidden)
|
|
211
|
+
return { forbidden };
|
|
212
|
+
headers[key] = text;
|
|
213
|
+
}
|
|
214
|
+
return {};
|
|
215
|
+
}
|
|
216
|
+
unmaskBodyBytes(body, hostname) {
|
|
217
|
+
let out = body;
|
|
218
|
+
const enc = new TextEncoder();
|
|
219
|
+
for (const [maskedValue, ms] of this.maskedToSecret) {
|
|
220
|
+
if (!matchesDomains(hostname, ms.domains))
|
|
221
|
+
continue;
|
|
222
|
+
const needle = enc.encode(maskedValue);
|
|
223
|
+
const replacement = enc.encode(ms.realValue);
|
|
224
|
+
out = replaceAllBytes(out, needle, replacement);
|
|
225
|
+
}
|
|
226
|
+
return { bytes: out };
|
|
227
|
+
}
|
|
228
|
+
scrubResponse(text) {
|
|
229
|
+
return this.scrubber(text);
|
|
230
|
+
}
|
|
231
|
+
scrubResponseBytes(bytes) {
|
|
232
|
+
let out = bytes;
|
|
233
|
+
const enc = new TextEncoder();
|
|
234
|
+
for (const [maskedValue, ms] of this.maskedToSecret) {
|
|
235
|
+
const needle = enc.encode(ms.realValue);
|
|
236
|
+
const replacement = enc.encode(maskedValue);
|
|
237
|
+
out = replaceAllBytes(out, needle, replacement);
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
scrubHeaders(headers) {
|
|
242
|
+
const out = {};
|
|
243
|
+
headers.forEach((v, k) => {
|
|
244
|
+
out[k] = this.scrubber(v);
|
|
245
|
+
});
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=secrets-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets-pipeline.js","sourceRoot":"","sources":["../src/secrets-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,IAAI,UAAU,EAClB,aAAa,EACb,cAAc,GAEf,MAAM,qBAAqB,CAAC;AAE7B,SAAS,YAAY,CAAC,QAAoB,EAAE,MAAkB,EAAE,IAAI,GAAG,CAAC;IACtE,KAAK,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC;gBAAE,SAAS,KAAK,CAAC;QACpD,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,SAAS,eAAe,CACtB,QAAoB,EACpB,MAAkB,EAClB,WAAuB;IAEvB,IAAI,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACxD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9C,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YAChE,MAAM;QACR,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AA4CD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,eAAe;IACV,SAAS,CAAS;IACjB,MAAM,CAAyB;IACxC,cAAc,GAAG,IAAI,GAAG,EAAwB,CAAC;IACjD,QAAQ,GAA6B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAEtD,YAAY,IAAyB;QACnC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAwB,CAAC;QAC7C,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YACtE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;gBACpB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,CAAC,CAAC,KAAK;gBAClB,WAAW;gBACX,OAAO,EAAE,CAAC,CAAC,OAAO;aACnB,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,MAAM,KAAK,GAAiB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACjE,SAAS,EAAE,EAAE,CAAC,SAAS;YACvB,WAAW,EAAE,EAAE,CAAC,WAAW;SAC5B,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,KAAa;QACvC,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,CAAC;IACtC,CAAC;IAED,gBAAgB;QACd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC3D,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,WAAW,EAAE,EAAE,CAAC,WAAW;YAC3B,OAAO,EAAE,EAAE,CAAC,OAAO;SACpB,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,IAAY,EAAE,QAAgB;QACnC,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACpD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAAE,SAAS;YAC5C,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC;YAChE,CAAC;YACD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,IAAY,EAAE,QAAgB;QACvC,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACpD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAAE,SAAS;YAC5C,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC;gBAAE,SAAS;YACpD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC;IAED,wBAAwB,CAAC,WAAmB,EAAE,QAAgB;QAC5D,MAAM,OAAO,GAAG,gBAAgB,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAC1C,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAChC,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAC7C,IAAI,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACnC,IAAI,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACpC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC1C,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC;gBAC9E,CAAC;gBACD,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;oBAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;gBAClF,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;oBAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;gBAClF,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAC5C,OAAO,EAAE,KAAK,EAAE,SAAS,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;IACvD,CAAC;IAED,8BAA8B,CAAC,MAAc;QAC3C,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;QAEjE,IAAI,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;oBACtC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;gBAC7E,CAAC;gBACD,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC/B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;oBAClD,OAAO,GAAG,IAAI,CAAC;gBACjB,CAAC;gBACD,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC/B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;oBAClD,OAAO,GAAG,IAAI,CAAC;gBACjB,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7F,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QACrB,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QACrB,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,sBAAsB,EAAE,SAAS,EAAE,CAAC;IACvE,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,OAA+B,EAAE,QAAgB;QAC7D,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACjD,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,eAAe,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnE,MAAM,KAAK,GAAG,IAAI,CAAC,wBAAwB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAC3D,IAAI,KAAK,CAAC,SAAS;oBAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC3D,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;gBAC3B,SAAS;YACX,CAAC;YACD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YACvD,IAAI,SAAS;gBAAE,OAAO,EAAE,SAAS,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,eAAe,CAAC,IAAgB,EAAE,QAAgB;QAChD,IAAI,GAAG,GAAG,IAAI,CAAC;QACf,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACpD,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC;gBAAE,SAAS;YACpD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACvC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;YAC7C,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACxB,CAAC;IAED,aAAa,CAAC,IAAY;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,kBAAkB,CAAC,KAAiB;QAClC,IAAI,GAAG,GAAG,KAAK,CAAC;QAChB,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACpD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;YACxC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC5C,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,YAAY,CAAC,OAAgB;QAC3B,MAAM,GAAG,GAA2B,EAAE,CAAC;QACvC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACvB,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
|
|
@@ -3,7 +3,8 @@ import { createServer } from 'http';
|
|
|
3
3
|
import { createServer as createNetServer } from 'net';
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
5
|
import { existsSync, readFileSync } from 'fs';
|
|
6
|
-
import { join, resolve } from 'path';
|
|
6
|
+
import { join, resolve, dirname } from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
7
8
|
import { Readable, Transform } from 'stream';
|
|
8
9
|
import { StringDecoder } from 'string_decoder';
|
|
9
10
|
import { fileURLToPath } from 'url';
|
|
@@ -18,7 +19,9 @@ import { FileLogger } from './file-logger.js';
|
|
|
18
19
|
import { CliLogDedup } from './cli-log-dedup.js';
|
|
19
20
|
import { EnvSecretStore } from './secrets/env-secret-store.js';
|
|
20
21
|
import { SecretProxyManager } from './secrets/proxy-manager.js';
|
|
22
|
+
import { OauthSecretStore } from './secrets/oauth-secret-store.js';
|
|
21
23
|
import { handleDaSignAndForward, handleS3SignAndForward } from './secrets/sign-and-forward.js';
|
|
24
|
+
import { readOrCreateSessionId } from './secrets/session-id-file.js';
|
|
22
25
|
import { FETCH_PROXY_SKIP_HEADERS } from './fetch-proxy-headers.js';
|
|
23
26
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
24
27
|
const PROJECT_ROOT = resolve(__dirname, '..', '..');
|
|
@@ -440,7 +443,6 @@ async function main() {
|
|
|
440
443
|
console.error(error instanceof Error ? error.message : String(error));
|
|
441
444
|
process.exit(1);
|
|
442
445
|
}
|
|
443
|
-
throw new Error('unreachable');
|
|
444
446
|
})();
|
|
445
447
|
const chromePath = findChromeExecutable({
|
|
446
448
|
executablePreference: !DEV_MODE && !chromeProfile.id ? 'installed' : 'chrome-for-testing',
|
|
@@ -489,7 +491,16 @@ async function main() {
|
|
|
489
491
|
});
|
|
490
492
|
}
|
|
491
493
|
// 3. Set up express app with request logging
|
|
492
|
-
const
|
|
494
|
+
const sessionDir = RUNTIME_FLAGS.envFile
|
|
495
|
+
? dirname(RUNTIME_FLAGS.envFile)
|
|
496
|
+
: join(homedir(), '.slicc');
|
|
497
|
+
const sessionId = readOrCreateSessionId(sessionDir);
|
|
498
|
+
const oauthStore = new OauthSecretStore();
|
|
499
|
+
// Env-file secrets (~/.slicc/secrets.env) feed the fetch-proxy mask
|
|
500
|
+
// pipeline alongside OAuth tokens. The same instance is reused below
|
|
501
|
+
// for /api/secrets and handleS3SignAndForward.
|
|
502
|
+
const secretStore = new EnvSecretStore(RUNTIME_FLAGS.envFile ?? undefined);
|
|
503
|
+
const secretProxy = new SecretProxyManager(secretStore, sessionId, oauthStore);
|
|
493
504
|
try {
|
|
494
505
|
await secretProxy.reload();
|
|
495
506
|
if (secretProxy.hasSecrets()) {
|
|
@@ -792,8 +803,9 @@ async function main() {
|
|
|
792
803
|
});
|
|
793
804
|
res.json({ ok: true });
|
|
794
805
|
});
|
|
795
|
-
// Secret management API — direct .env file access (no browser needed)
|
|
796
|
-
|
|
806
|
+
// Secret management API — direct .env file access (no browser needed).
|
|
807
|
+
// `secretStore` was created above and wired into `secretProxy` so the
|
|
808
|
+
// fetch-proxy and the management API share one source of truth.
|
|
797
809
|
app.get('/api/secrets', (_req, res) => {
|
|
798
810
|
try {
|
|
799
811
|
const entries = secretStore.list();
|
|
@@ -873,6 +885,31 @@ async function main() {
|
|
|
873
885
|
.json({ error: err instanceof Error ? err.message : 'Failed to get masked secrets' });
|
|
874
886
|
}
|
|
875
887
|
});
|
|
888
|
+
// OAuth secret update — stores access token from OAuth login flow
|
|
889
|
+
app.post('/api/secrets/oauth-update', express.json(), async (req, res) => {
|
|
890
|
+
const { providerId, accessToken, domains } = req.body ?? {};
|
|
891
|
+
if (typeof providerId !== 'string' ||
|
|
892
|
+
typeof accessToken !== 'string' ||
|
|
893
|
+
!Array.isArray(domains) ||
|
|
894
|
+
domains.length === 0) {
|
|
895
|
+
return res.status(400).json({ error: 'bad-request' });
|
|
896
|
+
}
|
|
897
|
+
const name = `oauth.${providerId}.token`;
|
|
898
|
+
oauthStore.set(name, accessToken, domains);
|
|
899
|
+
await secretProxy.reload();
|
|
900
|
+
const masked = secretProxy.getMaskedEntries().find((e) => e.name === name)?.maskedValue;
|
|
901
|
+
res.json({ providerId, name, maskedValue: masked, domains });
|
|
902
|
+
});
|
|
903
|
+
// OAuth secret deletion — removes access token on logout
|
|
904
|
+
app.delete('/api/secrets/oauth/:providerId', async (req, res) => {
|
|
905
|
+
const name = `oauth.${req.params.providerId}.token`;
|
|
906
|
+
if (!oauthStore.list().some((e) => e.name === name)) {
|
|
907
|
+
return res.status(404).json({ error: 'not-found' });
|
|
908
|
+
}
|
|
909
|
+
oauthStore.delete(name);
|
|
910
|
+
await secretProxy.reload();
|
|
911
|
+
res.status(204).end();
|
|
912
|
+
});
|
|
876
913
|
// Fetch proxy — forwards cross-origin requests from the browser to bypass CORS.
|
|
877
914
|
// Used by just-bash's curl which calls the browser's fetch() API.
|
|
878
915
|
// Note: express.json() may have already parsed the body, so we check req.body first.
|
|
@@ -986,6 +1023,23 @@ async function main() {
|
|
|
986
1023
|
return;
|
|
987
1024
|
}
|
|
988
1025
|
}
|
|
1026
|
+
// --- Secret injection: unmask URL-embedded credentials ---
|
|
1027
|
+
let cleanedUrl = targetUrl;
|
|
1028
|
+
if (secretProxy.hasSecrets()) {
|
|
1029
|
+
const credsResult = secretProxy.extractAndUnmaskUrlCredentials(targetUrl);
|
|
1030
|
+
if (credsResult.forbidden) {
|
|
1031
|
+
res.setHeader('X-Proxy-Error', '1');
|
|
1032
|
+
res.status(403).json({
|
|
1033
|
+
error: `Secret "${credsResult.forbidden.secretName}" is not allowed for domain "${credsResult.forbidden.hostname}"`,
|
|
1034
|
+
});
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
cleanedUrl = credsResult.url;
|
|
1038
|
+
// Attach synthetic Authorization if the URL had credentials and the header isn't already set
|
|
1039
|
+
if (credsResult.syntheticAuthorization && !('authorization' in headers)) {
|
|
1040
|
+
headers.authorization = credsResult.syntheticAuthorization;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
989
1043
|
if (Object.keys(headers).length > 0)
|
|
990
1044
|
fetchInit.headers = headers;
|
|
991
1045
|
if (rawBody.length > 0 && !['GET', 'HEAD'].includes(req.method)) {
|
|
@@ -994,7 +1048,24 @@ async function main() {
|
|
|
994
1048
|
// (safe/meaningless) rather than rejecting. This avoids false 403s when
|
|
995
1049
|
// LLM conversation context contains masked secrets sent to non-matching
|
|
996
1050
|
// domains like Bedrock.
|
|
997
|
-
|
|
1051
|
+
//
|
|
1052
|
+
// Skip the unmask for non-text content (git packfiles, octet-stream,
|
|
1053
|
+
// ZIPs, images, …) — `Buffer.toString('utf-8')` on arbitrary bytes
|
|
1054
|
+
// replaces invalid sequences with U+FFFD, silently corrupting the
|
|
1055
|
+
// payload. Masked values are hex strings with known prefixes; they
|
|
1056
|
+
// do not appear in deflated git packfiles or other compressed binary,
|
|
1057
|
+
// so skipping is safe.
|
|
1058
|
+
const reqCt = (headers['content-type'] ?? headers['Content-Type'] ?? '').toLowerCase();
|
|
1059
|
+
const reqIsText = !reqCt ||
|
|
1060
|
+
reqCt.startsWith('text/') ||
|
|
1061
|
+
reqCt.includes('json') ||
|
|
1062
|
+
reqCt.includes('xml') ||
|
|
1063
|
+
reqCt.includes('javascript') ||
|
|
1064
|
+
reqCt.includes('ecmascript') ||
|
|
1065
|
+
reqCt.includes('html') ||
|
|
1066
|
+
reqCt.includes('css') ||
|
|
1067
|
+
reqCt.includes('svg');
|
|
1068
|
+
if (reqIsText && secretProxy.hasSecrets()) {
|
|
998
1069
|
const bodyStr = rawBody.toString('utf-8');
|
|
999
1070
|
const bodyResult = secretProxy.unmaskBody(bodyStr, targetHostname);
|
|
1000
1071
|
rawBody = Buffer.from(bodyResult.text, 'utf-8');
|
|
@@ -1020,7 +1091,7 @@ async function main() {
|
|
|
1020
1091
|
};
|
|
1021
1092
|
res.on('close', onClientClose);
|
|
1022
1093
|
fetchInit.signal = abortController.signal;
|
|
1023
|
-
const upstream = await fetch(
|
|
1094
|
+
const upstream = await fetch(cleanedUrl, fetchInit);
|
|
1024
1095
|
// Forward status, prevent browser caching of proxy responses
|
|
1025
1096
|
res.status(upstream.status);
|
|
1026
1097
|
res.setHeader('Cache-Control', 'no-store, no-cache');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { Secret, SecretEntry, SecretStore } from './types.js';
|
|
1
|
+
export type { Secret, SecretEntry, SecretStore, MaskedSecret } from './types.js';
|
|
2
2
|
export { EnvSecretStore } from './env-secret-store.js';
|
|
3
|
-
export { matchDomain, matchesDomains } from '
|
|
3
|
+
export { domainMatches as matchDomain, matchesDomains } from '../_shared/index.js';
|
|
4
4
|
export { parseEnvFile, serializeEnvFile } from './env-file.js';
|
|
5
|
-
export { SecretProxyManager
|
|
5
|
+
export { SecretProxyManager } from './proxy-manager.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { EnvSecretStore } from './env-secret-store.js';
|
|
2
|
-
export { matchDomain, matchesDomains } from '
|
|
2
|
+
export { domainMatches as matchDomain, matchesDomains } from '../_shared/index.js';
|
|
3
3
|
export { parseEnvFile, serializeEnvFile } from './env-file.js';
|
|
4
4
|
export { SecretProxyManager } from './proxy-manager.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface OauthEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
value: string;
|
|
4
|
+
domains: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare class OauthSecretStore {
|
|
7
|
+
private entries;
|
|
8
|
+
set(name: string, value: string, domains: string[]): void;
|
|
9
|
+
delete(name: string): void;
|
|
10
|
+
list(): OauthEntry[];
|
|
11
|
+
get(name: string): string | undefined;
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class OauthSecretStore {
|
|
2
|
+
entries = new Map();
|
|
3
|
+
set(name, value, domains) {
|
|
4
|
+
if (!Array.isArray(domains) || domains.length === 0) {
|
|
5
|
+
throw new Error('OauthSecretStore: domains must be non-empty');
|
|
6
|
+
}
|
|
7
|
+
this.entries.set(name, { name, value, domains });
|
|
8
|
+
}
|
|
9
|
+
delete(name) {
|
|
10
|
+
this.entries.delete(name);
|
|
11
|
+
}
|
|
12
|
+
list() {
|
|
13
|
+
return Array.from(this.entries.values());
|
|
14
|
+
}
|
|
15
|
+
get(name) {
|
|
16
|
+
return this.entries.get(name)?.value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -4,45 +4,27 @@
|
|
|
4
4
|
*
|
|
5
5
|
* On init: loads all secrets, generates session-scoped masked values,
|
|
6
6
|
* and builds lookup tables for fast replacement.
|
|
7
|
+
*
|
|
8
|
+
* As of Task 1.4, this is a thin wrapper around SecretsPipeline from @slicc/shared-ts.
|
|
7
9
|
*/
|
|
8
|
-
import { EnvSecretStore } from './env-secret-store.js';
|
|
9
|
-
|
|
10
|
-
name: string;
|
|
11
|
-
maskedValue: string;
|
|
12
|
-
realValue: string;
|
|
13
|
-
domains: string[];
|
|
14
|
-
}
|
|
10
|
+
import { type EnvSecretStore } from './env-secret-store.js';
|
|
11
|
+
import { type OauthSecretStore } from './oauth-secret-store.js';
|
|
15
12
|
export declare class SecretProxyManager {
|
|
16
|
-
private readonly
|
|
17
|
-
private readonly
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* Load secrets from the store and generate masked values.
|
|
25
|
-
* Call once on startup and again whenever secrets change.
|
|
26
|
-
*/
|
|
13
|
+
private readonly pipeline;
|
|
14
|
+
private readonly _sessionId;
|
|
15
|
+
private _envStore?;
|
|
16
|
+
private _oauthStore?;
|
|
17
|
+
constructor(store?: EnvSecretStore, sessionId?: string, oauthStore?: OauthSecretStore);
|
|
18
|
+
private buildSource;
|
|
19
|
+
setOauthStore(store: OauthSecretStore): void;
|
|
20
|
+
get sessionId(): string;
|
|
27
21
|
reload(): Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Check if any secrets are loaded.
|
|
30
|
-
*/
|
|
31
22
|
hasSecrets(): boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Get all masked entries (name + maskedValue + domains) for env population.
|
|
34
|
-
*/
|
|
35
23
|
getMaskedEntries(): Array<{
|
|
36
24
|
name: string;
|
|
37
25
|
maskedValue: string;
|
|
38
26
|
domains: string[];
|
|
39
27
|
}>;
|
|
40
|
-
/**
|
|
41
|
-
* Unmask a text blob: replace masked values with real values.
|
|
42
|
-
* Validates each secret against the target hostname.
|
|
43
|
-
*
|
|
44
|
-
* @returns { text, forbidden } — forbidden is set if a secret was blocked.
|
|
45
|
-
*/
|
|
46
28
|
unmask(text: string, targetHostname: string): {
|
|
47
29
|
text: string;
|
|
48
30
|
forbidden?: {
|
|
@@ -50,34 +32,16 @@ export declare class SecretProxyManager {
|
|
|
50
32
|
hostname: string;
|
|
51
33
|
};
|
|
52
34
|
};
|
|
53
|
-
/**
|
|
54
|
-
* Unmask body text: replace masked values with real values when domain matches.
|
|
55
|
-
* When domain does NOT match, the masked value is left as-is (not rejected).
|
|
56
|
-
* This is safe because the masked value is meaningless to the remote service —
|
|
57
|
-
* it's typically just conversation context sent to an LLM API.
|
|
58
|
-
*/
|
|
59
35
|
unmaskBody(text: string, targetHostname: string): {
|
|
60
36
|
text: string;
|
|
61
37
|
};
|
|
62
|
-
/**
|
|
63
|
-
* Unmask headers in-place. Returns forbidden info if blocked.
|
|
64
|
-
*/
|
|
65
38
|
unmaskHeaders(headers: Record<string, string>, targetHostname: string): {
|
|
66
39
|
forbidden?: {
|
|
67
40
|
secretName: string;
|
|
68
41
|
hostname: string;
|
|
69
42
|
};
|
|
70
43
|
};
|
|
71
|
-
|
|
72
|
-
* Scrub real secret values from response text → masked values.
|
|
73
|
-
*/
|
|
44
|
+
extractAndUnmaskUrlCredentials(rawUrl: string): import("../_shared/index.js").ExtractedUrlCreds;
|
|
74
45
|
scrubResponse(text: string): string;
|
|
75
|
-
/**
|
|
76
|
-
* Scrub headers: replace real values with masked in header values.
|
|
77
|
-
*/
|
|
78
46
|
scrubHeaders(headers: Headers): Record<string, string>;
|
|
79
|
-
/**
|
|
80
|
-
* Look up a secret by its masked value.
|
|
81
|
-
*/
|
|
82
|
-
getByMaskedValue(maskedValue: string): MaskedSecret | undefined;
|
|
83
47
|
}
|