@id-wispera/core 0.1.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/README.md +268 -0
- package/dist/audit.d.ts +68 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +252 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychainProvider.d.ts +40 -0
- package/dist/auth/keychainProvider.d.ts.map +1 -0
- package/dist/auth/keychainProvider.js +98 -0
- package/dist/auth/keychainProvider.js.map +1 -0
- package/dist/auth/passphraseProvider.d.ts +80 -0
- package/dist/auth/passphraseProvider.d.ts.map +1 -0
- package/dist/auth/passphraseProvider.js +188 -0
- package/dist/auth/passphraseProvider.js.map +1 -0
- package/dist/auth/sessionTokenManager.d.ts +106 -0
- package/dist/auth/sessionTokenManager.d.ts.map +1 -0
- package/dist/auth/sessionTokenManager.js +263 -0
- package/dist/auth/sessionTokenManager.js.map +1 -0
- package/dist/delegation.d.ts +81 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +299 -0
- package/dist/delegation.js.map +1 -0
- package/dist/detection.d.ts +35 -0
- package/dist/detection.d.ts.map +1 -0
- package/dist/detection.js +474 -0
- package/dist/detection.js.map +1 -0
- package/dist/exec/execManager.d.ts +60 -0
- package/dist/exec/execManager.d.ts.map +1 -0
- package/dist/exec/execManager.js +226 -0
- package/dist/exec/execManager.js.map +1 -0
- package/dist/exec/index.d.ts +6 -0
- package/dist/exec/index.d.ts.map +1 -0
- package/dist/exec/index.js +5 -0
- package/dist/exec/index.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/base.d.ts +64 -0
- package/dist/integrations/base.d.ts.map +1 -0
- package/dist/integrations/base.js +173 -0
- package/dist/integrations/base.js.map +1 -0
- package/dist/integrations/envMapping.d.ts +47 -0
- package/dist/integrations/envMapping.d.ts.map +1 -0
- package/dist/integrations/envMapping.js +174 -0
- package/dist/integrations/envMapping.js.map +1 -0
- package/dist/integrations/google-a2a.d.ts +48 -0
- package/dist/integrations/google-a2a.d.ts.map +1 -0
- package/dist/integrations/google-a2a.js +108 -0
- package/dist/integrations/google-a2a.js.map +1 -0
- package/dist/integrations/index.d.ts +14 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +14 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/langchain.d.ts +38 -0
- package/dist/integrations/langchain.d.ts.map +1 -0
- package/dist/integrations/langchain.js +45 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/openai-agents.d.ts +76 -0
- package/dist/integrations/openai-agents.d.ts.map +1 -0
- package/dist/integrations/openai-agents.js +95 -0
- package/dist/integrations/openai-agents.js.map +1 -0
- package/dist/integrations/slack.d.ts +59 -0
- package/dist/integrations/slack.d.ts.map +1 -0
- package/dist/integrations/slack.js +113 -0
- package/dist/integrations/slack.js.map +1 -0
- package/dist/integrations/types.d.ts +107 -0
- package/dist/integrations/types.d.ts.map +1 -0
- package/dist/integrations/types.js +6 -0
- package/dist/integrations/types.js.map +1 -0
- package/dist/locations.d.ts +157 -0
- package/dist/locations.d.ts.map +1 -0
- package/dist/locations.js +733 -0
- package/dist/locations.js.map +1 -0
- package/dist/passport.d.ts +70 -0
- package/dist/passport.d.ts.map +1 -0
- package/dist/passport.js +429 -0
- package/dist/passport.js.map +1 -0
- package/dist/policy.d.ts +80 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +392 -0
- package/dist/policy.js.map +1 -0
- package/dist/providers/openclaw.d.ts +80 -0
- package/dist/providers/openclaw.d.ts.map +1 -0
- package/dist/providers/openclaw.js +712 -0
- package/dist/providers/openclaw.js.map +1 -0
- package/dist/provisioning/adminPassport.d.ts +51 -0
- package/dist/provisioning/adminPassport.d.ts.map +1 -0
- package/dist/provisioning/adminPassport.js +101 -0
- package/dist/provisioning/adminPassport.js.map +1 -0
- package/dist/provisioning/index.d.ts +81 -0
- package/dist/provisioning/index.d.ts.map +1 -0
- package/dist/provisioning/index.js +141 -0
- package/dist/provisioning/index.js.map +1 -0
- package/dist/provisioning/provider.d.ts +59 -0
- package/dist/provisioning/provider.d.ts.map +1 -0
- package/dist/provisioning/provider.js +52 -0
- package/dist/provisioning/provider.js.map +1 -0
- package/dist/provisioning/providers/anthropic.d.ts +32 -0
- package/dist/provisioning/providers/anthropic.d.ts.map +1 -0
- package/dist/provisioning/providers/anthropic.js +116 -0
- package/dist/provisioning/providers/anthropic.js.map +1 -0
- package/dist/provisioning/providers/aws.d.ts +29 -0
- package/dist/provisioning/providers/aws.d.ts.map +1 -0
- package/dist/provisioning/providers/aws.js +455 -0
- package/dist/provisioning/providers/aws.js.map +1 -0
- package/dist/provisioning/providers/azure-entra.d.ts +32 -0
- package/dist/provisioning/providers/azure-entra.d.ts.map +1 -0
- package/dist/provisioning/providers/azure-entra.js +312 -0
- package/dist/provisioning/providers/azure-entra.js.map +1 -0
- package/dist/provisioning/providers/github.d.ts +24 -0
- package/dist/provisioning/providers/github.d.ts.map +1 -0
- package/dist/provisioning/providers/github.js +219 -0
- package/dist/provisioning/providers/github.js.map +1 -0
- package/dist/provisioning/providers/google-cloud.d.ts +34 -0
- package/dist/provisioning/providers/google-cloud.d.ts.map +1 -0
- package/dist/provisioning/providers/google-cloud.js +366 -0
- package/dist/provisioning/providers/google-cloud.js.map +1 -0
- package/dist/provisioning/providers/openai.d.ts +29 -0
- package/dist/provisioning/providers/openai.d.ts.map +1 -0
- package/dist/provisioning/providers/openai.js +263 -0
- package/dist/provisioning/providers/openai.js.map +1 -0
- package/dist/provisioning/providers/sendgrid.d.ts +27 -0
- package/dist/provisioning/providers/sendgrid.d.ts.map +1 -0
- package/dist/provisioning/providers/sendgrid.js +186 -0
- package/dist/provisioning/providers/sendgrid.js.map +1 -0
- package/dist/provisioning/providers/twilio.d.ts +27 -0
- package/dist/provisioning/providers/twilio.d.ts.map +1 -0
- package/dist/provisioning/providers/twilio.js +194 -0
- package/dist/provisioning/providers/twilio.js.map +1 -0
- package/dist/provisioning/types.d.ts +274 -0
- package/dist/provisioning/types.d.ts.map +1 -0
- package/dist/provisioning/types.js +6 -0
- package/dist/provisioning/types.js.map +1 -0
- package/dist/sharing.d.ts +60 -0
- package/dist/sharing.d.ts.map +1 -0
- package/dist/sharing.js +305 -0
- package/dist/sharing.js.map +1 -0
- package/dist/types.d.ts +396 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +88 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +45 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +110 -0
- package/dist/utils.js.map +1 -0
- package/dist/vault.d.ts +151 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +499 -0
- package/dist/vault.js.map +1 -0
- package/package.json +117 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Wispera OpenClaw Provider
|
|
3
|
+
* Specialized scanner for OpenClaw credential locations
|
|
4
|
+
*/
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { sortByRisk, getRiskEmoji, getRiskLabel, } from '../locations.js';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// OpenClaw Path Constants
|
|
10
|
+
// ============================================================================
|
|
11
|
+
export const OPENCLAW_BASE_PATH = join(homedir(), '.openclaw');
|
|
12
|
+
export const OPENCLAW_PATHS = {
|
|
13
|
+
/** Base OpenClaw directory */
|
|
14
|
+
base: OPENCLAW_BASE_PATH,
|
|
15
|
+
/** WhatsApp credentials directory */
|
|
16
|
+
whatsapp: join(OPENCLAW_BASE_PATH, 'credentials', 'whatsapp'),
|
|
17
|
+
/** Main credentials directory */
|
|
18
|
+
credentials: join(OPENCLAW_BASE_PATH, 'credentials'),
|
|
19
|
+
/** Agents directory */
|
|
20
|
+
agents: join(OPENCLAW_BASE_PATH, 'agents'),
|
|
21
|
+
/** Main config file */
|
|
22
|
+
config: join(OPENCLAW_BASE_PATH, 'openclaw.json'),
|
|
23
|
+
/** OAuth tokens */
|
|
24
|
+
oauth: join(OPENCLAW_BASE_PATH, 'credentials', 'oauth.json'),
|
|
25
|
+
};
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// OpenClaw Detection
|
|
28
|
+
// ============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* Check if OpenClaw is installed
|
|
31
|
+
*/
|
|
32
|
+
export async function isOpenClawInstalled() {
|
|
33
|
+
const fs = await import('fs/promises');
|
|
34
|
+
try {
|
|
35
|
+
await fs.access(OPENCLAW_BASE_PATH);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check file permissions and return warning if insecure
|
|
44
|
+
*/
|
|
45
|
+
export async function checkFilePermissions(filePath) {
|
|
46
|
+
const fs = await import('fs/promises');
|
|
47
|
+
try {
|
|
48
|
+
const stats = await fs.stat(filePath);
|
|
49
|
+
const mode = stats.mode;
|
|
50
|
+
// Check if world or group readable (not 600 or 700)
|
|
51
|
+
const worldReadable = (mode & 0o004) !== 0;
|
|
52
|
+
const groupReadable = (mode & 0o040) !== 0;
|
|
53
|
+
if (worldReadable) {
|
|
54
|
+
return `File is world-readable (mode: ${(mode & 0o777).toString(8)}). Run: chmod 600 "${filePath}"`;
|
|
55
|
+
}
|
|
56
|
+
if (groupReadable) {
|
|
57
|
+
return `File is group-readable (mode: ${(mode & 0o777).toString(8)}). Run: chmod 600 "${filePath}"`;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check directory permissions
|
|
67
|
+
*/
|
|
68
|
+
export async function checkDirectoryPermissions(dirPath) {
|
|
69
|
+
const fs = await import('fs/promises');
|
|
70
|
+
try {
|
|
71
|
+
const stats = await fs.stat(dirPath);
|
|
72
|
+
const mode = stats.mode;
|
|
73
|
+
const worldReadable = (mode & 0o004) !== 0;
|
|
74
|
+
const groupReadable = (mode & 0o040) !== 0;
|
|
75
|
+
if (worldReadable || groupReadable) {
|
|
76
|
+
return `Directory has insecure permissions (mode: ${(mode & 0o777).toString(8)}). Run: chmod 700 "${dirPath}"`;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Scan all OpenClaw credential locations
|
|
86
|
+
*/
|
|
87
|
+
export async function scanOpenClaw() {
|
|
88
|
+
const fs = await import('fs/promises');
|
|
89
|
+
const result = {
|
|
90
|
+
installed: false,
|
|
91
|
+
installPath: OPENCLAW_BASE_PATH,
|
|
92
|
+
credentials: [],
|
|
93
|
+
locationResults: [],
|
|
94
|
+
summary: {
|
|
95
|
+
total: 0,
|
|
96
|
+
governed: 0,
|
|
97
|
+
criticalCount: 0,
|
|
98
|
+
highCount: 0,
|
|
99
|
+
mediumCount: 0,
|
|
100
|
+
lowCount: 0,
|
|
101
|
+
},
|
|
102
|
+
permissionWarnings: [],
|
|
103
|
+
unreadablePaths: [],
|
|
104
|
+
};
|
|
105
|
+
// Check if OpenClaw is installed
|
|
106
|
+
result.installed = await isOpenClawInstalled();
|
|
107
|
+
if (!result.installed) {
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
// Check base directory permissions
|
|
111
|
+
const baseDirWarning = await checkDirectoryPermissions(OPENCLAW_BASE_PATH);
|
|
112
|
+
if (baseDirWarning) {
|
|
113
|
+
result.permissionWarnings.push(baseDirWarning);
|
|
114
|
+
}
|
|
115
|
+
// Scan each location
|
|
116
|
+
await Promise.all([
|
|
117
|
+
scanWhatsAppCredentials(result, fs),
|
|
118
|
+
scanChannelAllowlists(result, fs),
|
|
119
|
+
scanAuthProfiles(result, fs),
|
|
120
|
+
scanOAuthTokens(result, fs),
|
|
121
|
+
scanOpenClawConfig(result, fs),
|
|
122
|
+
]);
|
|
123
|
+
// Calculate summary
|
|
124
|
+
for (const cred of result.credentials) {
|
|
125
|
+
result.summary.total++;
|
|
126
|
+
switch (cred.classification.riskLevel) {
|
|
127
|
+
case 'critical':
|
|
128
|
+
result.summary.criticalCount++;
|
|
129
|
+
break;
|
|
130
|
+
case 'high':
|
|
131
|
+
result.summary.highCount++;
|
|
132
|
+
break;
|
|
133
|
+
case 'medium':
|
|
134
|
+
result.summary.mediumCount++;
|
|
135
|
+
break;
|
|
136
|
+
case 'low':
|
|
137
|
+
result.summary.lowCount++;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Sort credentials by risk
|
|
142
|
+
result.credentials = sortByRisk(result.credentials);
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Scan WhatsApp credentials
|
|
147
|
+
*/
|
|
148
|
+
async function scanWhatsAppCredentials(result, fs) {
|
|
149
|
+
const locationResult = {
|
|
150
|
+
path: OPENCLAW_PATHS.whatsapp,
|
|
151
|
+
exists: false,
|
|
152
|
+
readable: false,
|
|
153
|
+
credentials: [],
|
|
154
|
+
};
|
|
155
|
+
try {
|
|
156
|
+
await fs.access(OPENCLAW_PATHS.whatsapp);
|
|
157
|
+
locationResult.exists = true;
|
|
158
|
+
const accounts = await fs.readdir(OPENCLAW_PATHS.whatsapp);
|
|
159
|
+
locationResult.readable = true;
|
|
160
|
+
for (const accountId of accounts) {
|
|
161
|
+
const credsPath = join(OPENCLAW_PATHS.whatsapp, accountId, 'creds.json');
|
|
162
|
+
try {
|
|
163
|
+
const content = await fs.readFile(credsPath, 'utf-8');
|
|
164
|
+
const permWarning = await checkFilePermissions(credsPath);
|
|
165
|
+
const cred = parseWhatsAppCreds(credsPath, content, accountId);
|
|
166
|
+
if (cred) {
|
|
167
|
+
if (permWarning) {
|
|
168
|
+
cred.insecurePermissions = true;
|
|
169
|
+
result.permissionWarnings.push(permWarning);
|
|
170
|
+
}
|
|
171
|
+
locationResult.credentials.push(cred);
|
|
172
|
+
result.credentials.push(cred);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
if (err.code === 'EACCES') {
|
|
177
|
+
result.unreadablePaths.push({ path: credsPath, error: 'Permission denied' });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
if (err.code === 'ENOENT') {
|
|
184
|
+
// Directory doesn't exist, that's fine
|
|
185
|
+
}
|
|
186
|
+
else if (err.code === 'EACCES') {
|
|
187
|
+
locationResult.error = 'Permission denied';
|
|
188
|
+
result.unreadablePaths.push({ path: OPENCLAW_PATHS.whatsapp, error: 'Permission denied' });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
result.locationResults.push(locationResult);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Parse WhatsApp credentials file
|
|
195
|
+
*/
|
|
196
|
+
function parseWhatsAppCreds(filePath, content, accountId) {
|
|
197
|
+
try {
|
|
198
|
+
const data = JSON.parse(content);
|
|
199
|
+
const phoneNumber = data.me?.id?.split(':')[0] || accountId;
|
|
200
|
+
const botName = data.me?.name || 'Unknown';
|
|
201
|
+
return {
|
|
202
|
+
provider: 'openclaw',
|
|
203
|
+
filePath,
|
|
204
|
+
classification: {
|
|
205
|
+
name: `WhatsApp Session (${accountId})`,
|
|
206
|
+
credentialType: 'session-keys',
|
|
207
|
+
visaType: 'access',
|
|
208
|
+
riskLevel: 'high',
|
|
209
|
+
platforms: ['openclaw', 'whatsapp'],
|
|
210
|
+
tags: ['openclaw', 'whatsapp', 'session', 'imported'],
|
|
211
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
212
|
+
},
|
|
213
|
+
rawValue: content,
|
|
214
|
+
metadata: {
|
|
215
|
+
accountId,
|
|
216
|
+
phoneNumber,
|
|
217
|
+
botName,
|
|
218
|
+
registrationId: data.registrationId,
|
|
219
|
+
hasNoiseKey: !!data.noiseKey,
|
|
220
|
+
hasSignedIdentityKey: !!data.signedIdentityKey,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Scan channel allowlists
|
|
230
|
+
*/
|
|
231
|
+
async function scanChannelAllowlists(result, fs) {
|
|
232
|
+
const locationResult = {
|
|
233
|
+
path: OPENCLAW_PATHS.credentials,
|
|
234
|
+
exists: false,
|
|
235
|
+
readable: false,
|
|
236
|
+
credentials: [],
|
|
237
|
+
};
|
|
238
|
+
try {
|
|
239
|
+
await fs.access(OPENCLAW_PATHS.credentials);
|
|
240
|
+
locationResult.exists = true;
|
|
241
|
+
const files = await fs.readdir(OPENCLAW_PATHS.credentials);
|
|
242
|
+
locationResult.readable = true;
|
|
243
|
+
for (const file of files) {
|
|
244
|
+
if (file.endsWith('-allowFrom.json')) {
|
|
245
|
+
const filePath = join(OPENCLAW_PATHS.credentials, file);
|
|
246
|
+
try {
|
|
247
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
248
|
+
const channel = file.replace('-allowFrom.json', '');
|
|
249
|
+
const cred = parseAllowlist(filePath, content, channel);
|
|
250
|
+
if (cred) {
|
|
251
|
+
const permWarning = await checkFilePermissions(filePath);
|
|
252
|
+
if (permWarning) {
|
|
253
|
+
cred.insecurePermissions = true;
|
|
254
|
+
result.permissionWarnings.push(permWarning);
|
|
255
|
+
}
|
|
256
|
+
locationResult.credentials.push(cred);
|
|
257
|
+
result.credentials.push(cred);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
if (err.code === 'EACCES') {
|
|
262
|
+
result.unreadablePaths.push({ path: filePath, error: 'Permission denied' });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
if (err.code === 'EACCES') {
|
|
270
|
+
locationResult.error = 'Permission denied';
|
|
271
|
+
result.unreadablePaths.push({ path: OPENCLAW_PATHS.credentials, error: 'Permission denied' });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
result.locationResults.push(locationResult);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Parse channel allowlist file
|
|
278
|
+
*/
|
|
279
|
+
function parseAllowlist(filePath, content, channel) {
|
|
280
|
+
try {
|
|
281
|
+
const data = JSON.parse(content);
|
|
282
|
+
const pairedCount = Object.keys(data).length;
|
|
283
|
+
return {
|
|
284
|
+
provider: 'openclaw',
|
|
285
|
+
filePath,
|
|
286
|
+
classification: {
|
|
287
|
+
name: `Pairing: ${channel} (${pairedCount} paired users)`,
|
|
288
|
+
credentialType: 'custom',
|
|
289
|
+
visaType: 'access',
|
|
290
|
+
riskLevel: 'low',
|
|
291
|
+
platforms: ['openclaw', channel],
|
|
292
|
+
tags: ['openclaw', channel, 'allowlist', 'imported'],
|
|
293
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
294
|
+
},
|
|
295
|
+
rawValue: content,
|
|
296
|
+
metadata: {
|
|
297
|
+
channel,
|
|
298
|
+
pairedCount,
|
|
299
|
+
pairedUsers: Object.entries(data).map(([id, info]) => ({
|
|
300
|
+
id,
|
|
301
|
+
name: info.name,
|
|
302
|
+
pairedAt: info.pairedAt,
|
|
303
|
+
})),
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Scan auth profiles (LLM API keys)
|
|
313
|
+
*/
|
|
314
|
+
async function scanAuthProfiles(result, fs) {
|
|
315
|
+
const locationResult = {
|
|
316
|
+
path: OPENCLAW_PATHS.agents,
|
|
317
|
+
exists: false,
|
|
318
|
+
readable: false,
|
|
319
|
+
credentials: [],
|
|
320
|
+
};
|
|
321
|
+
try {
|
|
322
|
+
await fs.access(OPENCLAW_PATHS.agents);
|
|
323
|
+
locationResult.exists = true;
|
|
324
|
+
const agents = await fs.readdir(OPENCLAW_PATHS.agents);
|
|
325
|
+
locationResult.readable = true;
|
|
326
|
+
for (const agentId of agents) {
|
|
327
|
+
const authProfilesPath = join(OPENCLAW_PATHS.agents, agentId, 'agent', 'auth-profiles.json');
|
|
328
|
+
try {
|
|
329
|
+
const content = await fs.readFile(authProfilesPath, 'utf-8');
|
|
330
|
+
const creds = parseAuthProfiles(authProfilesPath, content, agentId);
|
|
331
|
+
for (const cred of creds) {
|
|
332
|
+
const permWarning = await checkFilePermissions(authProfilesPath);
|
|
333
|
+
if (permWarning) {
|
|
334
|
+
cred.insecurePermissions = true;
|
|
335
|
+
if (!result.permissionWarnings.includes(permWarning)) {
|
|
336
|
+
result.permissionWarnings.push(permWarning);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
locationResult.credentials.push(cred);
|
|
340
|
+
result.credentials.push(cred);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
if (err.code === 'EACCES') {
|
|
345
|
+
result.unreadablePaths.push({ path: authProfilesPath, error: 'Permission denied' });
|
|
346
|
+
}
|
|
347
|
+
// ENOENT is fine - agent may not have auth profiles
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch (err) {
|
|
352
|
+
if (err.code === 'EACCES') {
|
|
353
|
+
locationResult.error = 'Permission denied';
|
|
354
|
+
result.unreadablePaths.push({ path: OPENCLAW_PATHS.agents, error: 'Permission denied' });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
result.locationResults.push(locationResult);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Parse auth profiles file
|
|
361
|
+
*/
|
|
362
|
+
function parseAuthProfiles(filePath, content, agentId) {
|
|
363
|
+
const credentials = [];
|
|
364
|
+
try {
|
|
365
|
+
const data = JSON.parse(content);
|
|
366
|
+
for (const [provider, profile] of Object.entries(data)) {
|
|
367
|
+
if (profile.type === 'api-key' && profile.key) {
|
|
368
|
+
const keyPreview = profile.key.substring(0, 10) + '...' + profile.key.substring(profile.key.length - 4);
|
|
369
|
+
credentials.push({
|
|
370
|
+
provider: 'openclaw',
|
|
371
|
+
filePath,
|
|
372
|
+
classification: {
|
|
373
|
+
name: `${capitalizeFirst(provider)} API Key (${keyPreview})`,
|
|
374
|
+
credentialType: 'api-key',
|
|
375
|
+
visaType: 'privilege',
|
|
376
|
+
riskLevel: 'critical',
|
|
377
|
+
platforms: ['openclaw', 'mcp', provider],
|
|
378
|
+
tags: ['openclaw', provider, 'api-key', 'llm', 'imported'],
|
|
379
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
380
|
+
context: { agentId, model: profile.model },
|
|
381
|
+
},
|
|
382
|
+
rawValue: profile.key,
|
|
383
|
+
metadata: {
|
|
384
|
+
agentId,
|
|
385
|
+
provider,
|
|
386
|
+
model: profile.model,
|
|
387
|
+
keyType: profile.type,
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
// Invalid JSON
|
|
395
|
+
}
|
|
396
|
+
return credentials;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Scan OAuth tokens
|
|
400
|
+
*/
|
|
401
|
+
async function scanOAuthTokens(result, fs) {
|
|
402
|
+
const locationResult = {
|
|
403
|
+
path: OPENCLAW_PATHS.oauth,
|
|
404
|
+
exists: false,
|
|
405
|
+
readable: false,
|
|
406
|
+
credentials: [],
|
|
407
|
+
};
|
|
408
|
+
try {
|
|
409
|
+
const content = await fs.readFile(OPENCLAW_PATHS.oauth, 'utf-8');
|
|
410
|
+
locationResult.exists = true;
|
|
411
|
+
locationResult.readable = true;
|
|
412
|
+
const permWarning = await checkFilePermissions(OPENCLAW_PATHS.oauth);
|
|
413
|
+
const creds = parseOAuthTokens(OPENCLAW_PATHS.oauth, content);
|
|
414
|
+
for (const cred of creds) {
|
|
415
|
+
if (permWarning) {
|
|
416
|
+
cred.insecurePermissions = true;
|
|
417
|
+
if (!result.permissionWarnings.includes(permWarning)) {
|
|
418
|
+
result.permissionWarnings.push(permWarning);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
locationResult.credentials.push(cred);
|
|
422
|
+
result.credentials.push(cred);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
if (err.code === 'EACCES') {
|
|
427
|
+
locationResult.error = 'Permission denied';
|
|
428
|
+
result.unreadablePaths.push({ path: OPENCLAW_PATHS.oauth, error: 'Permission denied' });
|
|
429
|
+
}
|
|
430
|
+
// ENOENT is fine
|
|
431
|
+
}
|
|
432
|
+
result.locationResults.push(locationResult);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Parse OAuth tokens file
|
|
436
|
+
*/
|
|
437
|
+
function parseOAuthTokens(filePath, content) {
|
|
438
|
+
const credentials = [];
|
|
439
|
+
try {
|
|
440
|
+
const data = JSON.parse(content);
|
|
441
|
+
for (const [provider, tokens] of Object.entries(data)) {
|
|
442
|
+
if (tokens.access_token) {
|
|
443
|
+
const tokenPreview = tokens.access_token.substring(0, 10) + '...';
|
|
444
|
+
const expiresAt = tokens.expires_at ? new Date(tokens.expires_at * 1000) : undefined;
|
|
445
|
+
const isExpired = expiresAt && expiresAt < new Date();
|
|
446
|
+
credentials.push({
|
|
447
|
+
provider: 'openclaw',
|
|
448
|
+
filePath,
|
|
449
|
+
classification: {
|
|
450
|
+
name: `${capitalizeFirst(provider)} OAuth (${tokenPreview}${isExpired ? ' EXPIRED' : ''})`,
|
|
451
|
+
credentialType: 'oauth-token',
|
|
452
|
+
visaType: 'access',
|
|
453
|
+
riskLevel: 'medium',
|
|
454
|
+
platforms: ['openclaw', provider],
|
|
455
|
+
tags: ['openclaw', provider, 'oauth', 'imported', ...(isExpired ? ['expired'] : [])],
|
|
456
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
457
|
+
},
|
|
458
|
+
rawValue: tokens.access_token,
|
|
459
|
+
metadata: {
|
|
460
|
+
provider,
|
|
461
|
+
hasRefreshToken: !!tokens.refresh_token,
|
|
462
|
+
tokenType: tokens.token_type,
|
|
463
|
+
expiresAt: expiresAt?.toISOString(),
|
|
464
|
+
isExpired,
|
|
465
|
+
},
|
|
466
|
+
expiresAt,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
// Invalid JSON
|
|
473
|
+
}
|
|
474
|
+
return credentials;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Scan OpenClaw config file
|
|
478
|
+
*/
|
|
479
|
+
async function scanOpenClawConfig(result, fs) {
|
|
480
|
+
const locationResult = {
|
|
481
|
+
path: OPENCLAW_PATHS.config,
|
|
482
|
+
exists: false,
|
|
483
|
+
readable: false,
|
|
484
|
+
credentials: [],
|
|
485
|
+
};
|
|
486
|
+
try {
|
|
487
|
+
const content = await fs.readFile(OPENCLAW_PATHS.config, 'utf-8');
|
|
488
|
+
locationResult.exists = true;
|
|
489
|
+
locationResult.readable = true;
|
|
490
|
+
const permWarning = await checkFilePermissions(OPENCLAW_PATHS.config);
|
|
491
|
+
const creds = parseOpenClawConfig(OPENCLAW_PATHS.config, content);
|
|
492
|
+
for (const cred of creds) {
|
|
493
|
+
if (permWarning) {
|
|
494
|
+
cred.insecurePermissions = true;
|
|
495
|
+
if (!result.permissionWarnings.includes(permWarning)) {
|
|
496
|
+
result.permissionWarnings.push(permWarning);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
locationResult.credentials.push(cred);
|
|
500
|
+
result.credentials.push(cred);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
if (err.code === 'EACCES') {
|
|
505
|
+
locationResult.error = 'Permission denied';
|
|
506
|
+
result.unreadablePaths.push({ path: OPENCLAW_PATHS.config, error: 'Permission denied' });
|
|
507
|
+
}
|
|
508
|
+
// ENOENT is fine
|
|
509
|
+
}
|
|
510
|
+
result.locationResults.push(locationResult);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Parse OpenClaw config file
|
|
514
|
+
*/
|
|
515
|
+
function parseOpenClawConfig(filePath, content) {
|
|
516
|
+
const credentials = [];
|
|
517
|
+
try {
|
|
518
|
+
const data = JSON.parse(content);
|
|
519
|
+
// Parse channel tokens
|
|
520
|
+
if (data.channels) {
|
|
521
|
+
// Telegram
|
|
522
|
+
if (data.channels.telegram?.token) {
|
|
523
|
+
const token = data.channels.telegram.token;
|
|
524
|
+
const tokenPreview = token.substring(0, 7) + '...' + token.substring(token.length - 4);
|
|
525
|
+
credentials.push({
|
|
526
|
+
provider: 'openclaw',
|
|
527
|
+
filePath,
|
|
528
|
+
classification: {
|
|
529
|
+
name: `Telegram Bot Token (${tokenPreview})`,
|
|
530
|
+
credentialType: 'bot-token',
|
|
531
|
+
visaType: 'access',
|
|
532
|
+
riskLevel: 'high',
|
|
533
|
+
platforms: ['openclaw', 'telegram'],
|
|
534
|
+
tags: ['openclaw', 'telegram', 'bot-token', 'imported'],
|
|
535
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
536
|
+
},
|
|
537
|
+
rawValue: token,
|
|
538
|
+
metadata: { channel: 'telegram' },
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
// Slack
|
|
542
|
+
if (data.channels.slack) {
|
|
543
|
+
if (data.channels.slack.botToken) {
|
|
544
|
+
const token = data.channels.slack.botToken;
|
|
545
|
+
const tokenPreview = token.substring(0, 10) + '...' + token.substring(token.length - 4);
|
|
546
|
+
credentials.push({
|
|
547
|
+
provider: 'openclaw',
|
|
548
|
+
filePath,
|
|
549
|
+
classification: {
|
|
550
|
+
name: `Slack Bot Token (${tokenPreview})`,
|
|
551
|
+
credentialType: 'bot-token',
|
|
552
|
+
visaType: 'access',
|
|
553
|
+
riskLevel: 'high',
|
|
554
|
+
platforms: ['openclaw', 'slack'],
|
|
555
|
+
tags: ['openclaw', 'slack', 'bot-token', 'imported'],
|
|
556
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
557
|
+
},
|
|
558
|
+
rawValue: token,
|
|
559
|
+
metadata: { channel: 'slack', tokenType: 'bot' },
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
if (data.channels.slack.appToken) {
|
|
563
|
+
const token = data.channels.slack.appToken;
|
|
564
|
+
const tokenPreview = token.substring(0, 10) + '...' + token.substring(token.length - 4);
|
|
565
|
+
credentials.push({
|
|
566
|
+
provider: 'openclaw',
|
|
567
|
+
filePath,
|
|
568
|
+
classification: {
|
|
569
|
+
name: `Slack App Token (${tokenPreview})`,
|
|
570
|
+
credentialType: 'bot-token',
|
|
571
|
+
visaType: 'access',
|
|
572
|
+
riskLevel: 'high',
|
|
573
|
+
platforms: ['openclaw', 'slack'],
|
|
574
|
+
tags: ['openclaw', 'slack', 'app-token', 'imported'],
|
|
575
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
576
|
+
},
|
|
577
|
+
rawValue: token,
|
|
578
|
+
metadata: { channel: 'slack', tokenType: 'app' },
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
// Discord
|
|
583
|
+
if (data.channels.discord?.token) {
|
|
584
|
+
const token = data.channels.discord.token;
|
|
585
|
+
const tokenPreview = token.substring(0, 10) + '...' + token.substring(token.length - 4);
|
|
586
|
+
credentials.push({
|
|
587
|
+
provider: 'openclaw',
|
|
588
|
+
filePath,
|
|
589
|
+
classification: {
|
|
590
|
+
name: `Discord Bot Token (${tokenPreview})`,
|
|
591
|
+
credentialType: 'bot-token',
|
|
592
|
+
visaType: 'access',
|
|
593
|
+
riskLevel: 'high',
|
|
594
|
+
platforms: ['openclaw', 'discord'],
|
|
595
|
+
tags: ['openclaw', 'discord', 'bot-token', 'imported'],
|
|
596
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
597
|
+
},
|
|
598
|
+
rawValue: token,
|
|
599
|
+
metadata: { channel: 'discord' },
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// Gateway token
|
|
604
|
+
if (data.gateway?.token) {
|
|
605
|
+
const token = data.gateway.token;
|
|
606
|
+
const tokenPreview = token.substring(0, 6) + '...' + token.substring(token.length - 4);
|
|
607
|
+
credentials.push({
|
|
608
|
+
provider: 'openclaw',
|
|
609
|
+
filePath,
|
|
610
|
+
classification: {
|
|
611
|
+
name: `Gateway Token (${tokenPreview})`,
|
|
612
|
+
credentialType: 'api-key',
|
|
613
|
+
visaType: 'privilege',
|
|
614
|
+
riskLevel: 'high',
|
|
615
|
+
platforms: ['openclaw'],
|
|
616
|
+
tags: ['openclaw', 'gateway', 'admin', 'imported'],
|
|
617
|
+
issuingAuthority: 'OpenClaw (self-managed)',
|
|
618
|
+
},
|
|
619
|
+
rawValue: token,
|
|
620
|
+
metadata: { gatewayPort: data.gateway.port },
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
catch {
|
|
625
|
+
// Invalid JSON
|
|
626
|
+
}
|
|
627
|
+
return credentials;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Helper to capitalize first letter
|
|
631
|
+
*/
|
|
632
|
+
function capitalizeFirst(str) {
|
|
633
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
634
|
+
}
|
|
635
|
+
// ============================================================================
|
|
636
|
+
// Import Helpers
|
|
637
|
+
// ============================================================================
|
|
638
|
+
/**
|
|
639
|
+
* Convert discovered credential to passport input
|
|
640
|
+
*/
|
|
641
|
+
export function toPassportInput(cred, humanOwner, agentId) {
|
|
642
|
+
const delegationChain = [
|
|
643
|
+
{
|
|
644
|
+
from: humanOwner,
|
|
645
|
+
to: 'OpenClaw Instance',
|
|
646
|
+
grantedAt: new Date().toISOString(),
|
|
647
|
+
scope: ['*'],
|
|
648
|
+
},
|
|
649
|
+
];
|
|
650
|
+
if (agentId || cred.metadata.agentId) {
|
|
651
|
+
delegationChain.push({
|
|
652
|
+
from: 'OpenClaw Instance',
|
|
653
|
+
to: `Agent: ${agentId || cred.metadata.agentId}`,
|
|
654
|
+
grantedAt: new Date().toISOString(),
|
|
655
|
+
scope: ['*'],
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
return {
|
|
659
|
+
name: `OpenClaw - ${cred.classification.name}`,
|
|
660
|
+
agentId: (agentId || cred.metadata.agentId),
|
|
661
|
+
credentialType: cred.classification.credentialType,
|
|
662
|
+
credentialValue: cred.rawValue,
|
|
663
|
+
visaType: cred.classification.visaType,
|
|
664
|
+
issuingAuthority: cred.classification.issuingAuthority,
|
|
665
|
+
scope: ['*'],
|
|
666
|
+
platforms: cred.classification.platforms,
|
|
667
|
+
validFrom: new Date().toISOString(),
|
|
668
|
+
validUntil: cred.expiresAt?.toISOString(),
|
|
669
|
+
delegationChain,
|
|
670
|
+
humanOwner,
|
|
671
|
+
tags: cred.classification.tags,
|
|
672
|
+
notes: `Imported from OpenClaw: ${cred.filePath}`,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
// ============================================================================
|
|
676
|
+
// Formatting Helpers (for CLI output)
|
|
677
|
+
// ============================================================================
|
|
678
|
+
/**
|
|
679
|
+
* Format credential for table display
|
|
680
|
+
*/
|
|
681
|
+
export function formatCredentialRow(cred) {
|
|
682
|
+
return {
|
|
683
|
+
credential: cred.classification.name,
|
|
684
|
+
type: formatCredentialType(cred.classification.credentialType),
|
|
685
|
+
visaType: formatVisaType(cred.classification.visaType),
|
|
686
|
+
riskLevel: `${getRiskEmoji(cred.classification.riskLevel)} ${getRiskLabel(cred.classification.riskLevel)}`,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
function formatCredentialType(type) {
|
|
690
|
+
const typeMap = {
|
|
691
|
+
'api-key': 'API Key',
|
|
692
|
+
'oauth-token': 'OAuth Token',
|
|
693
|
+
'session-keys': 'Session Keys',
|
|
694
|
+
'bot-token': 'Bot Token',
|
|
695
|
+
'private-key': 'Private Key',
|
|
696
|
+
custom: 'Allowlist',
|
|
697
|
+
secret: 'Secret',
|
|
698
|
+
};
|
|
699
|
+
return typeMap[type] || type;
|
|
700
|
+
}
|
|
701
|
+
function formatVisaType(type) {
|
|
702
|
+
const typeMap = {
|
|
703
|
+
access: 'Access Visa',
|
|
704
|
+
privilege: 'Privilege',
|
|
705
|
+
data: 'Data',
|
|
706
|
+
compliance: 'Compliance',
|
|
707
|
+
ecosystem: 'Ecosystem',
|
|
708
|
+
custom: 'Custom',
|
|
709
|
+
};
|
|
710
|
+
return typeMap[type] || type;
|
|
711
|
+
}
|
|
712
|
+
//# sourceMappingURL=openclaw.js.map
|