agent-messenger 2.10.0 → 2.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/package.json +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js +15 -2
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/shared/chromium/decryptor.d.ts +6 -0
- package/dist/src/shared/chromium/decryptor.d.ts.map +1 -1
- package/dist/src/shared/chromium/decryptor.js +26 -6
- package/dist/src/shared/chromium/decryptor.js.map +1 -1
- package/e2e/channeltalk.e2e.test.ts +13 -13
- package/e2e/channeltalkbot.e2e.test.ts +13 -13
- package/e2e/discord.e2e.test.ts +24 -24
- package/e2e/discordbot.e2e.test.ts +16 -16
- package/e2e/instagram.e2e.test.ts +10 -10
- package/e2e/kakaotalk.e2e.test.ts +7 -7
- package/e2e/line.e2e.test.ts +8 -8
- package/e2e/slack.e2e.test.ts +34 -34
- package/e2e/slackbot.e2e.test.ts +14 -14
- package/e2e/teams.e2e.test.ts +23 -23
- package/e2e/telegram.e2e.test.ts +8 -8
- package/e2e/webex.e2e.test.ts +14 -14
- package/e2e/whatsapp.e2e.test.ts +8 -8
- package/e2e/whatsappbot.e2e.test.ts +6 -6
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/channeltalk/client.test.ts +26 -26
- package/src/platforms/channeltalk/commands/auth.test.ts +16 -16
- package/src/platforms/channeltalk/commands/bot.test.ts +2 -2
- package/src/platforms/channeltalk/commands/chat.test.ts +3 -3
- package/src/platforms/channeltalk/commands/group.test.ts +4 -4
- package/src/platforms/channeltalk/commands/manager.test.ts +2 -2
- package/src/platforms/channeltalk/commands/message.test.ts +17 -17
- package/src/platforms/channeltalk/commands/snapshot.test.ts +7 -7
- package/src/platforms/channeltalk/commands/whoami.test.ts +3 -3
- package/src/platforms/channeltalk/credential-manager.test.ts +18 -18
- package/src/platforms/channeltalk/ensure-auth.test.ts +5 -5
- package/src/platforms/channeltalk/index.test.ts +23 -23
- package/src/platforms/channeltalk/token-extractor.test.ts +21 -21
- package/src/platforms/channeltalk/types.test.ts +12 -12
- package/src/platforms/channeltalkbot/client.test.ts +14 -14
- package/src/platforms/channeltalkbot/commands/auth.test.ts +16 -16
- package/src/platforms/channeltalkbot/commands/bot.test.ts +6 -6
- package/src/platforms/channeltalkbot/commands/chat.test.ts +9 -9
- package/src/platforms/channeltalkbot/commands/group.test.ts +6 -6
- package/src/platforms/channeltalkbot/commands/manager.test.ts +3 -3
- package/src/platforms/channeltalkbot/commands/message.test.ts +10 -10
- package/src/platforms/channeltalkbot/commands/snapshot.test.ts +7 -7
- package/src/platforms/channeltalkbot/commands/whoami.test.ts +4 -4
- package/src/platforms/channeltalkbot/credential-manager.test.ts +27 -27
- package/src/platforms/channeltalkbot/index.test.ts +15 -15
- package/src/platforms/discord/client.test.ts +28 -28
- package/src/platforms/discord/commands/auth.test.ts +7 -7
- package/src/platforms/discord/commands/channel.test.ts +7 -7
- package/src/platforms/discord/commands/dm.test.ts +4 -4
- package/src/platforms/discord/commands/file.test.ts +4 -4
- package/src/platforms/discord/commands/friend.test.ts +6 -6
- package/src/platforms/discord/commands/member.test.ts +5 -5
- package/src/platforms/discord/commands/mention.test.ts +5 -5
- package/src/platforms/discord/commands/message.test.ts +9 -9
- package/src/platforms/discord/commands/note.test.ts +6 -6
- package/src/platforms/discord/commands/profile.test.ts +4 -4
- package/src/platforms/discord/commands/reaction.test.ts +5 -5
- package/src/platforms/discord/commands/server.test.ts +7 -7
- package/src/platforms/discord/commands/snapshot.test.ts +6 -6
- package/src/platforms/discord/commands/thread.test.ts +6 -6
- package/src/platforms/discord/commands/user.test.ts +5 -5
- package/src/platforms/discord/commands/whoami.test.ts +6 -6
- package/src/platforms/discord/credential-manager.test.ts +16 -16
- package/src/platforms/discord/ensure-auth.test.ts +8 -8
- package/src/platforms/discord/index.test.ts +17 -17
- package/src/platforms/discord/listener.test.ts +33 -33
- package/src/platforms/discord/token-extractor.test.ts +53 -53
- package/src/platforms/discord/types.test.ts +26 -26
- package/src/platforms/discordbot/client.test.ts +31 -31
- package/src/platforms/discordbot/commands/auth.test.ts +18 -18
- package/src/platforms/discordbot/commands/channel.test.ts +11 -11
- package/src/platforms/discordbot/commands/file.test.ts +7 -7
- package/src/platforms/discordbot/commands/message.test.ts +25 -25
- package/src/platforms/discordbot/commands/reaction.test.ts +6 -6
- package/src/platforms/discordbot/commands/server.test.ts +12 -12
- package/src/platforms/discordbot/commands/snapshot.test.ts +13 -13
- package/src/platforms/discordbot/commands/thread.test.ts +10 -10
- package/src/platforms/discordbot/commands/user.test.ts +9 -9
- package/src/platforms/discordbot/commands/whoami.test.ts +4 -4
- package/src/platforms/discordbot/credential-manager.test.ts +28 -28
- package/src/platforms/instagram/client.test.ts +18 -18
- package/src/platforms/instagram/commands/auth.test.ts +11 -11
- package/src/platforms/instagram/commands/chat.test.ts +6 -6
- package/src/platforms/instagram/commands/message.test.ts +11 -11
- package/src/platforms/instagram/commands/shared.test.ts +12 -12
- package/src/platforms/instagram/commands/whoami.test.ts +3 -3
- package/src/platforms/instagram/credential-manager.test.ts +21 -21
- package/src/platforms/instagram/ensure-auth.test.ts +4 -4
- package/src/platforms/instagram/index.test.ts +9 -9
- package/src/platforms/instagram/listener.test.ts +8 -8
- package/src/platforms/instagram/token-extractor.test.ts +35 -35
- package/src/platforms/kakaotalk/client.test.ts +33 -33
- package/src/platforms/kakaotalk/commands/auth.test.ts +11 -11
- package/src/platforms/kakaotalk/commands/chat.test.ts +6 -6
- package/src/platforms/kakaotalk/commands/message.test.ts +7 -7
- package/src/platforms/kakaotalk/commands/whoami.test.ts +5 -5
- package/src/platforms/kakaotalk/credential-manager.test.ts +15 -15
- package/src/platforms/kakaotalk/index.test.ts +15 -15
- package/src/platforms/kakaotalk/listener.test.ts +17 -17
- package/src/platforms/line/client.test.ts +17 -17
- package/src/platforms/line/commands/auth.test.ts +8 -8
- package/src/platforms/line/commands/chat.test.ts +7 -7
- package/src/platforms/line/commands/friend.test.ts +6 -6
- package/src/platforms/line/commands/message.test.ts +7 -7
- package/src/platforms/line/commands/whoami.test.ts +6 -6
- package/src/platforms/line/credential-manager.test.ts +17 -17
- package/src/platforms/line/index.test.ts +10 -10
- package/src/platforms/line/listener.test.ts +15 -15
- package/src/platforms/line/types.test.ts +14 -14
- package/src/platforms/slack/cli.test.ts +8 -8
- package/src/platforms/slack/client.test.ts +151 -151
- package/src/platforms/slack/commands/activity.test.ts +13 -13
- package/src/platforms/slack/commands/auth.test.ts +34 -34
- package/src/platforms/slack/commands/bookmark.test.ts +9 -9
- package/src/platforms/slack/commands/channel.test.ts +17 -17
- package/src/platforms/slack/commands/drafts.test.ts +7 -7
- package/src/platforms/slack/commands/emoji.test.ts +3 -3
- package/src/platforms/slack/commands/file.test.ts +12 -12
- package/src/platforms/slack/commands/message.test.ts +19 -19
- package/src/platforms/slack/commands/pin.test.ts +7 -7
- package/src/platforms/slack/commands/reaction.test.ts +10 -10
- package/src/platforms/slack/commands/reminder.test.ts +9 -9
- package/src/platforms/slack/commands/saved.test.ts +7 -7
- package/src/platforms/slack/commands/sections.test.ts +5 -5
- package/src/platforms/slack/commands/snapshot.test.ts +13 -13
- package/src/platforms/slack/commands/unread.test.ts +6 -6
- package/src/platforms/slack/commands/user.test.ts +10 -10
- package/src/platforms/slack/commands/usergroup.test.ts +15 -15
- package/src/platforms/slack/commands/whoami.test.ts +6 -6
- package/src/platforms/slack/commands/workspace.test.ts +26 -26
- package/src/platforms/slack/credential-manager.test.ts +14 -14
- package/src/platforms/slack/ensure-auth.test.ts +21 -21
- package/src/platforms/slack/index.test.ts +12 -12
- package/src/platforms/slack/listener.test.ts +17 -17
- package/src/platforms/slack/token-extractor-node.test.ts +2 -2
- package/src/platforms/slack/token-extractor.test.ts +37 -37
- package/src/platforms/slack/types.test.ts +21 -21
- package/src/platforms/slackbot/client.test.ts +22 -22
- package/src/platforms/slackbot/commands/auth.test.ts +14 -14
- package/src/platforms/slackbot/commands/channel.test.ts +7 -7
- package/src/platforms/slackbot/commands/message.test.ts +13 -13
- package/src/platforms/slackbot/commands/reaction.test.ts +6 -6
- package/src/platforms/slackbot/commands/user.test.ts +7 -7
- package/src/platforms/slackbot/commands/whoami.test.ts +4 -4
- package/src/platforms/slackbot/credential-manager.test.ts +22 -22
- package/src/platforms/slackbot/types.test.ts +7 -7
- package/src/platforms/teams/client.test.ts +30 -30
- package/src/platforms/teams/commands/auth.test.ts +8 -8
- package/src/platforms/teams/commands/channel.test.ts +7 -7
- package/src/platforms/teams/commands/file.test.ts +4 -4
- package/src/platforms/teams/commands/message.test.ts +5 -5
- package/src/platforms/teams/commands/reaction.test.ts +4 -4
- package/src/platforms/teams/commands/snapshot.test.ts +7 -7
- package/src/platforms/teams/commands/team.test.ts +8 -8
- package/src/platforms/teams/commands/user.test.ts +4 -4
- package/src/platforms/teams/commands/whoami.test.ts +6 -6
- package/src/platforms/teams/credential-manager.test.ts +17 -17
- package/src/platforms/teams/ensure-auth.test.ts +13 -13
- package/src/platforms/teams/index.test.ts +15 -15
- package/src/platforms/teams/token-extractor.test.ts +219 -145
- package/src/platforms/teams/token-extractor.ts +13 -2
- package/src/platforms/teams/types.test.ts +26 -26
- package/src/platforms/telegram/app-config.test.ts +4 -4
- package/src/platforms/telegram/chat-utils.test.ts +12 -12
- package/src/platforms/telegram/client.test.ts +4 -4
- package/src/platforms/telegram/commands/auth.test.ts +16 -16
- package/src/platforms/telegram/commands/chat.test.ts +9 -9
- package/src/platforms/telegram/commands/message.test.ts +6 -6
- package/src/platforms/telegram/commands/shared.test.ts +3 -3
- package/src/platforms/telegram/commands/whoami.test.ts +3 -3
- package/src/platforms/telegram/credential-manager.test.ts +10 -10
- package/src/platforms/telegram/types.test.ts +6 -6
- package/src/platforms/webex/app-config.test.ts +8 -8
- package/src/platforms/webex/cli.test.ts +5 -5
- package/src/platforms/webex/client.test.ts +65 -65
- package/src/platforms/webex/commands/auth.test.ts +18 -18
- package/src/platforms/webex/commands/member.test.ts +5 -5
- package/src/platforms/webex/commands/message.test.ts +12 -12
- package/src/platforms/webex/commands/snapshot.test.ts +5 -5
- package/src/platforms/webex/commands/space.test.ts +10 -10
- package/src/platforms/webex/commands/whoami.test.ts +6 -6
- package/src/platforms/webex/credential-manager.test.ts +22 -22
- package/src/platforms/webex/encryption.test.ts +4 -4
- package/src/platforms/webex/ensure-auth.test.ts +5 -5
- package/src/platforms/webex/index.test.ts +5 -5
- package/src/platforms/webex/markdown-to-html.test.ts +33 -33
- package/src/platforms/webex/token-extractor.test.ts +23 -23
- package/src/platforms/webex/types.test.ts +27 -27
- package/src/platforms/wechatbot/client.test.ts +27 -27
- package/src/platforms/wechatbot/commands/auth.test.ts +15 -15
- package/src/platforms/wechatbot/commands/message.test.ts +8 -8
- package/src/platforms/wechatbot/commands/template.test.ts +9 -9
- package/src/platforms/wechatbot/commands/user.test.ts +7 -7
- package/src/platforms/wechatbot/commands/whoami.test.ts +5 -5
- package/src/platforms/wechatbot/credential-manager.test.ts +18 -18
- package/src/platforms/wechatbot/index.test.ts +10 -10
- package/src/platforms/wechatbot/types.test.ts +25 -25
- package/src/platforms/whatsapp/commands/auth.test.ts +13 -13
- package/src/platforms/whatsapp/commands/chat.test.ts +8 -8
- package/src/platforms/whatsapp/commands/message.test.ts +10 -10
- package/src/platforms/whatsapp/commands/whoami.test.ts +3 -3
- package/src/platforms/whatsapp/credential-manager.test.ts +23 -23
- package/src/platforms/whatsapp/ensure-auth.test.ts +4 -4
- package/src/platforms/whatsapp/index.test.ts +8 -8
- package/src/platforms/whatsapp/types.test.ts +42 -42
- package/src/platforms/whatsappbot/client.test.ts +27 -27
- package/src/platforms/whatsappbot/commands/auth.test.ts +14 -14
- package/src/platforms/whatsappbot/commands/message.test.ts +16 -16
- package/src/platforms/whatsappbot/commands/template.test.ts +9 -9
- package/src/platforms/whatsappbot/commands/whoami.test.ts +5 -5
- package/src/platforms/whatsappbot/credential-manager.test.ts +18 -18
- package/src/platforms/whatsappbot/index.test.ts +7 -7
- package/src/platforms/whatsappbot/types.test.ts +18 -18
- package/src/shared/chromium/browsers.test.ts +22 -22
- package/src/shared/chromium/cookie-reader.test.ts +13 -13
- package/src/shared/chromium/decryptor.test.ts +97 -32
- package/src/shared/chromium/decryptor.ts +27 -6
- package/src/shared/utils/concurrency.test.ts +6 -6
- package/src/shared/utils/derived-key-cache.test.ts +11 -11
- package/src/tui/utils.test.ts +31 -31
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database } from 'bun:sqlite'
|
|
2
|
-
import { afterEach, describe, expect, spyOn,
|
|
2
|
+
import { afterEach, describe, expect, spyOn, it } from 'bun:test'
|
|
3
3
|
import { createCipheriv, randomBytes } from 'node:crypto'
|
|
4
4
|
import * as fs from 'node:fs'
|
|
5
5
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
@@ -33,7 +33,7 @@ function createCookiesDb(
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
describe('TokenExtractor token deduplication', () => {
|
|
36
|
-
|
|
36
|
+
it('keeps first token per team and upgrades unknown team name', async () => {
|
|
37
37
|
// given — two .log entries for the same team: first has unknown name, second has a name
|
|
38
38
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-dedup-'))
|
|
39
39
|
tempDirs.push(slackDir)
|
|
@@ -59,7 +59,7 @@ describe('TokenExtractor token deduplication', () => {
|
|
|
59
59
|
expect(result[0].workspace_name).toBe('workspace-name')
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
it('prefers Local Storage token over IndexedDB token for same team', async () => {
|
|
63
63
|
// given — same team in Local Storage (valid) and IndexedDB (stale)
|
|
64
64
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-dedup-tier-'))
|
|
65
65
|
tempDirs.push(slackDir)
|
|
@@ -87,7 +87,7 @@ describe('TokenExtractor token deduplication', () => {
|
|
|
87
87
|
expect(result[0].workspace_name).toBe('valid-workspace')
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
it('prefers IndexedDB token when Local Storage has no token for team', async () => {
|
|
91
91
|
// given — token only in IndexedDB
|
|
92
92
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-dedup-idb-only-'))
|
|
93
93
|
tempDirs.push(slackDir)
|
|
@@ -108,7 +108,7 @@ describe('TokenExtractor token deduplication', () => {
|
|
|
108
108
|
expect(result[0].token).toBe(token)
|
|
109
109
|
})
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
it('prefers storage dir token over IndexedDB token for same team', async () => {
|
|
112
112
|
// given — structured JSON in storage dir vs raw token in IndexedDB
|
|
113
113
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-dedup-storage-'))
|
|
114
114
|
tempDirs.push(slackDir)
|
|
@@ -136,7 +136,7 @@ describe('TokenExtractor token deduplication', () => {
|
|
|
136
136
|
expect(result[0].workspace_name).toBe('storage-workspace')
|
|
137
137
|
})
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
it('prefers .log tokens over .ldb tokens for same team', async () => {
|
|
140
140
|
// given — same team ID in both .log (fresh) and .ldb (stale)
|
|
141
141
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-dedup-order-'))
|
|
142
142
|
tempDirs.push(slackDir)
|
|
@@ -160,7 +160,7 @@ describe('TokenExtractor token deduplication', () => {
|
|
|
160
160
|
expect(result[0].token).toBe(freshToken)
|
|
161
161
|
})
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
it('keeps all tokens with unknown teamId instead of merging them', async () => {
|
|
164
164
|
// given — two different tokens without team ID context (no T[A-Z0-9] nearby)
|
|
165
165
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-dedup-unknown-'))
|
|
166
166
|
tempDirs.push(slackDir)
|
|
@@ -203,7 +203,7 @@ describe('TokenExtractor LevelDB fragmentation markers', () => {
|
|
|
203
203
|
return Buffer.concat([prefix, segments[0] ? Buffer.concat(segments) : Buffer.alloc(0), suffix])
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
it('extracts token with old fragmentation marker [19 0d f0 NN]', async () => {
|
|
207
207
|
// given
|
|
208
208
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-marker-old-'))
|
|
209
209
|
tempDirs.push(slackDir)
|
|
@@ -225,7 +225,7 @@ describe('TokenExtractor LevelDB fragmentation markers', () => {
|
|
|
225
225
|
expect(result[0].token).toBe(`xoxc-1111111111-2222222222-3333333333-${hex64}`)
|
|
226
226
|
})
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
it('extracts token with new fragmentation marker [15 0b f0 43]', async () => {
|
|
229
229
|
// given — marker whose 4th byte (0x43 = "C") is a valid hex char
|
|
230
230
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-marker-new-'))
|
|
231
231
|
tempDirs.push(slackDir)
|
|
@@ -248,7 +248,7 @@ describe('TokenExtractor LevelDB fragmentation markers', () => {
|
|
|
248
248
|
expect(result[0].token).not.toContain('C')
|
|
249
249
|
})
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
it('extracts token with new fragmentation marker [15 0b f0 58]', async () => {
|
|
252
252
|
// given — marker whose 4th byte (0x58 = "X") is not a valid hex char
|
|
253
253
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-marker-58-'))
|
|
254
254
|
tempDirs.push(slackDir)
|
|
@@ -272,7 +272,7 @@ describe('TokenExtractor LevelDB fragmentation markers', () => {
|
|
|
272
272
|
})
|
|
273
273
|
|
|
274
274
|
describe('TokenExtractor Linux cookie decryption', () => {
|
|
275
|
-
|
|
275
|
+
it('decrypts v10 cookie using peanuts password on Linux', async () => {
|
|
276
276
|
// given — LevelDB with valid token + v10-encrypted cookie using Linux key
|
|
277
277
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-linux-'))
|
|
278
278
|
tempDirs.push(slackDir)
|
|
@@ -311,7 +311,7 @@ describe('TokenExtractor Linux cookie decryption', () => {
|
|
|
311
311
|
})
|
|
312
312
|
|
|
313
313
|
describe('TokenExtractor Linux v11 cookie decryption', () => {
|
|
314
|
-
|
|
314
|
+
it('decrypts v11 cookie using gnome-keyring password on Linux', () => {
|
|
315
315
|
// given — v11-prefixed cookie encrypted with a known keyring password
|
|
316
316
|
const { createCipheriv, pbkdf2Sync } = require('node:crypto')
|
|
317
317
|
const testPassword = 'test-gnome-keyring-password'
|
|
@@ -338,7 +338,7 @@ describe('TokenExtractor Linux v11 cookie decryption', () => {
|
|
|
338
338
|
keyringPasswordSpy.mockRestore()
|
|
339
339
|
})
|
|
340
340
|
|
|
341
|
-
|
|
341
|
+
it('falls back to peanuts key when keyring is unavailable for v11 cookie', () => {
|
|
342
342
|
// given — v11-prefixed cookie encrypted with peanuts (tests fallback code path)
|
|
343
343
|
const { createCipheriv, pbkdf2Sync } = require('node:crypto')
|
|
344
344
|
const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
|
|
@@ -363,7 +363,7 @@ describe('TokenExtractor Linux v11 cookie decryption', () => {
|
|
|
363
363
|
})
|
|
364
364
|
|
|
365
365
|
describe('TokenExtractor debug logging', () => {
|
|
366
|
-
|
|
366
|
+
it('calls debugLog callback during extraction', async () => {
|
|
367
367
|
// given
|
|
368
368
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-debug-'))
|
|
369
369
|
tempDirs.push(slackDir)
|
|
@@ -380,7 +380,7 @@ describe('TokenExtractor debug logging', () => {
|
|
|
380
380
|
expect(messages.length).toBeGreaterThan(0)
|
|
381
381
|
})
|
|
382
382
|
|
|
383
|
-
|
|
383
|
+
it('does not throw when debugLog is not provided', async () => {
|
|
384
384
|
// given
|
|
385
385
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-no-debug-'))
|
|
386
386
|
tempDirs.push(slackDir)
|
|
@@ -394,12 +394,12 @@ describe('TokenExtractor debug logging', () => {
|
|
|
394
394
|
})
|
|
395
395
|
|
|
396
396
|
describe('TokenExtractor Windows DPAPI', () => {
|
|
397
|
-
|
|
397
|
+
it('decryptDPAPI returns null on non-win32 platform', () => {
|
|
398
398
|
const extractor = new TokenExtractor('darwin', '/tmp/slack-test')
|
|
399
399
|
expect(extractor.decryptDPAPI(Buffer.from('test'))).toBeNull()
|
|
400
400
|
})
|
|
401
401
|
|
|
402
|
-
|
|
402
|
+
it('decryptV10CookieWindows decrypts AES-256-GCM with master key from Local State', () => {
|
|
403
403
|
// given — known master key and AES-256-GCM encrypted cookie
|
|
404
404
|
const masterKey = randomBytes(32)
|
|
405
405
|
|
|
@@ -423,7 +423,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
423
423
|
expect(extractor.tryDecryptCookie(encrypted)).toBe(plaintext)
|
|
424
424
|
})
|
|
425
425
|
|
|
426
|
-
|
|
426
|
+
it('decryptV10CookieWindows falls back to direct DPAPI when no Local State', () => {
|
|
427
427
|
class TestTokenExtractor extends TokenExtractor {
|
|
428
428
|
override getWindowsMasterKey(): null {
|
|
429
429
|
return null
|
|
@@ -439,7 +439,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
439
439
|
expect(extractor.tryDecryptCookie(encrypted)).toBe('xoxd-dpapiDirectCookie%2B')
|
|
440
440
|
})
|
|
441
441
|
|
|
442
|
-
|
|
442
|
+
it('tryDecryptCookie handles Windows pre-v80 cookies without version prefix', () => {
|
|
443
443
|
class TestTokenExtractor extends TokenExtractor {
|
|
444
444
|
override decryptDPAPI(_encrypted: Buffer): Buffer | null {
|
|
445
445
|
return Buffer.from('xoxd-preV80Cookie%2B')
|
|
@@ -452,7 +452,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
452
452
|
expect(extractor.tryDecryptCookie(encrypted)).toBe('xoxd-preV80Cookie%2B')
|
|
453
453
|
})
|
|
454
454
|
|
|
455
|
-
|
|
455
|
+
it('getWindowsMasterKey reads and decrypts key from Local State file', () => {
|
|
456
456
|
// given
|
|
457
457
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-win-'))
|
|
458
458
|
tempDirs.push(slackDir)
|
|
@@ -477,7 +477,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
477
477
|
expect(extractor.getWindowsMasterKey()).toEqual(fakeDecryptedKey)
|
|
478
478
|
})
|
|
479
479
|
|
|
480
|
-
|
|
480
|
+
it('getWindowsMasterKey returns null when Local State is missing', () => {
|
|
481
481
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-no-ls-'))
|
|
482
482
|
tempDirs.push(slackDir)
|
|
483
483
|
|
|
@@ -485,7 +485,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
485
485
|
expect(extractor.getWindowsMasterKey()).toBeNull()
|
|
486
486
|
})
|
|
487
487
|
|
|
488
|
-
|
|
488
|
+
it('getWindowsMasterKey returns null when encrypted_key has no DPAPI prefix', () => {
|
|
489
489
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-bad-ls-'))
|
|
490
490
|
tempDirs.push(slackDir)
|
|
491
491
|
|
|
@@ -496,7 +496,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
496
496
|
expect(extractor.getWindowsMasterKey()).toBeNull()
|
|
497
497
|
})
|
|
498
498
|
|
|
499
|
-
|
|
499
|
+
it('extract throws descriptive error when cookie file is locked (EBUSY)', async () => {
|
|
500
500
|
// given — LevelDB with a valid token but Cookies file locked by Slack
|
|
501
501
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-ebusy-'))
|
|
502
502
|
tempDirs.push(slackDir)
|
|
@@ -523,7 +523,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
523
523
|
}
|
|
524
524
|
})
|
|
525
525
|
|
|
526
|
-
|
|
526
|
+
it('extract decrypts Windows v10 cookies end-to-end with mocked DPAPI', async () => {
|
|
527
527
|
// given — SQLite DB with v10-encrypted cookie, Local State with master key, LevelDB with token
|
|
528
528
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-win-e2e-'))
|
|
529
529
|
tempDirs.push(slackDir)
|
|
@@ -582,7 +582,7 @@ describe('TokenExtractor Windows DPAPI', () => {
|
|
|
582
582
|
})
|
|
583
583
|
|
|
584
584
|
describe('TokenExtractor IndexedDB blob files', () => {
|
|
585
|
-
|
|
585
|
+
it('extracts token from blob file when not in LevelDB', async () => {
|
|
586
586
|
// given — token only exists in an IndexedDB blob file, not in LevelDB
|
|
587
587
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-blob-'))
|
|
588
588
|
tempDirs.push(slackDir)
|
|
@@ -604,7 +604,7 @@ describe('TokenExtractor IndexedDB blob files', () => {
|
|
|
604
604
|
expect(result[0].workspace_id).toBe('T12345678')
|
|
605
605
|
})
|
|
606
606
|
|
|
607
|
-
|
|
607
|
+
it('extracts tokens from both LevelDB and blob files for different teams', async () => {
|
|
608
608
|
// given — one workspace token in LevelDB, another in blob file
|
|
609
609
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-blob-multi-'))
|
|
610
610
|
tempDirs.push(slackDir)
|
|
@@ -632,7 +632,7 @@ describe('TokenExtractor IndexedDB blob files', () => {
|
|
|
632
632
|
expect(teamIds).toEqual(['TAAAAAAAA', 'TBBBBBBBBB'])
|
|
633
633
|
})
|
|
634
634
|
|
|
635
|
-
|
|
635
|
+
it('LevelDB token wins over blob file token for same team', async () => {
|
|
636
636
|
// given — same team in both LevelDB (.log = highest raw priority) and blob file
|
|
637
637
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-blob-dedup-'))
|
|
638
638
|
tempDirs.push(slackDir)
|
|
@@ -659,7 +659,7 @@ describe('TokenExtractor IndexedDB blob files', () => {
|
|
|
659
659
|
expect(result[0].token).toBe(ldbToken)
|
|
660
660
|
})
|
|
661
661
|
|
|
662
|
-
|
|
662
|
+
it('merges same token from LDB and blob with different teamIds', async () => {
|
|
663
663
|
// given — same token in LevelDB (correct teamId) and blob file (false-positive teamId from binary)
|
|
664
664
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-blob-dup-'))
|
|
665
665
|
tempDirs.push(slackDir)
|
|
@@ -685,7 +685,7 @@ describe('TokenExtractor IndexedDB blob files', () => {
|
|
|
685
685
|
expect(result[0].token).toBe(token)
|
|
686
686
|
})
|
|
687
687
|
|
|
688
|
-
|
|
688
|
+
it('skips blob files larger than 10MB', async () => {
|
|
689
689
|
// given — blob file exceeds size limit
|
|
690
690
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-blob-large-'))
|
|
691
691
|
tempDirs.push(slackDir)
|
|
@@ -710,7 +710,7 @@ describe('TokenExtractor IndexedDB blob files', () => {
|
|
|
710
710
|
})
|
|
711
711
|
|
|
712
712
|
describe('TokenExtractor getWorkspaceDomains', () => {
|
|
713
|
-
|
|
713
|
+
it('reads workspace domains from root-state.json', () => {
|
|
714
714
|
// given
|
|
715
715
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-domains-'))
|
|
716
716
|
tempDirs.push(slackDir)
|
|
@@ -735,7 +735,7 @@ describe('TokenExtractor getWorkspaceDomains', () => {
|
|
|
735
735
|
expect(domains).toEqual({ T111: 'acme-corp', T222: 'devteam' })
|
|
736
736
|
})
|
|
737
737
|
|
|
738
|
-
|
|
738
|
+
it('returns empty when root-state.json is missing', () => {
|
|
739
739
|
// given
|
|
740
740
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-no-rootstate-'))
|
|
741
741
|
tempDirs.push(slackDir)
|
|
@@ -748,7 +748,7 @@ describe('TokenExtractor getWorkspaceDomains', () => {
|
|
|
748
748
|
expect(domains).toEqual({})
|
|
749
749
|
})
|
|
750
750
|
|
|
751
|
-
|
|
751
|
+
it('returns empty when root-state.json has no workspaces', () => {
|
|
752
752
|
// given
|
|
753
753
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-empty-rootstate-'))
|
|
754
754
|
tempDirs.push(slackDir)
|
|
@@ -765,7 +765,7 @@ describe('TokenExtractor getWorkspaceDomains', () => {
|
|
|
765
765
|
expect(domains).toEqual({})
|
|
766
766
|
})
|
|
767
767
|
|
|
768
|
-
|
|
768
|
+
it('skips workspaces without domain field', () => {
|
|
769
769
|
// given
|
|
770
770
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-partial-rootstate-'))
|
|
771
771
|
tempDirs.push(slackDir)
|
|
@@ -792,7 +792,7 @@ describe('TokenExtractor getWorkspaceDomains', () => {
|
|
|
792
792
|
})
|
|
793
793
|
|
|
794
794
|
describe('TokenExtractor browser fallback', () => {
|
|
795
|
-
|
|
795
|
+
it('extractFromBrowsers returns empty array when no browser profiles have tokens', async () => {
|
|
796
796
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-nonexistent-'))
|
|
797
797
|
tempDirs.push(slackDir)
|
|
798
798
|
rmSync(slackDir, { recursive: true, force: true })
|
|
@@ -802,7 +802,7 @@ describe('TokenExtractor browser fallback', () => {
|
|
|
802
802
|
expect(result).toEqual([])
|
|
803
803
|
})
|
|
804
804
|
|
|
805
|
-
|
|
805
|
+
it('extract tries desktop before browser profiles', async () => {
|
|
806
806
|
// given — slackDir with LevelDB token data
|
|
807
807
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-fallback-desktop-'))
|
|
808
808
|
tempDirs.push(slackDir)
|
|
@@ -826,7 +826,7 @@ describe('TokenExtractor browser fallback', () => {
|
|
|
826
826
|
extractFromBrowsersSpy.mockRestore()
|
|
827
827
|
})
|
|
828
828
|
|
|
829
|
-
|
|
829
|
+
it('extract falls back to browser profiles when desktop has no tokens', async () => {
|
|
830
830
|
// given — empty slackDir (no tokens)
|
|
831
831
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-fallback-browser-'))
|
|
832
832
|
tempDirs.push(slackDir)
|
|
@@ -855,7 +855,7 @@ describe('TokenExtractor browser fallback', () => {
|
|
|
855
855
|
extractFromBrowsersSpy.mockRestore()
|
|
856
856
|
})
|
|
857
857
|
|
|
858
|
-
|
|
858
|
+
it('extract falls back to browser when slackDir does not exist', async () => {
|
|
859
859
|
// given — non-existent slackDir
|
|
860
860
|
const slackDir = '/nonexistent/slack/dir'
|
|
861
861
|
const hex64 = 'c'.repeat(64)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect,
|
|
1
|
+
import { expect, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
ConfigSchema,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
WorkspaceCredentialsSchema,
|
|
11
11
|
} from '@/platforms/slack/types'
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
it('SlackChannelSchema validates correct data', () => {
|
|
14
14
|
const validChannel = {
|
|
15
15
|
id: 'C123456',
|
|
16
16
|
name: 'general',
|
|
@@ -22,7 +22,7 @@ test('SlackChannelSchema validates correct data', () => {
|
|
|
22
22
|
expect(() => SlackChannelSchema.parse(validChannel)).not.toThrow()
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
it('SlackChannelSchema validates with optional fields', () => {
|
|
26
26
|
const validChannel = {
|
|
27
27
|
id: 'C123456',
|
|
28
28
|
name: 'general',
|
|
@@ -44,7 +44,7 @@ test('SlackChannelSchema validates with optional fields', () => {
|
|
|
44
44
|
expect(() => SlackChannelSchema.parse(validChannel)).not.toThrow()
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
it('SlackChannelSchema rejects missing required fields', () => {
|
|
48
48
|
const invalidChannel = {
|
|
49
49
|
id: 'C123456',
|
|
50
50
|
name: 'general',
|
|
@@ -52,7 +52,7 @@ test('SlackChannelSchema rejects missing required fields', () => {
|
|
|
52
52
|
expect(() => SlackChannelSchema.parse(invalidChannel)).toThrow()
|
|
53
53
|
})
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
it('SlackMessageSchema validates correct data', () => {
|
|
56
56
|
const validMessage = {
|
|
57
57
|
ts: '1234567890.123456',
|
|
58
58
|
text: 'Hello world',
|
|
@@ -61,7 +61,7 @@ test('SlackMessageSchema validates correct data', () => {
|
|
|
61
61
|
expect(() => SlackMessageSchema.parse(validMessage)).not.toThrow()
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
it('SlackMessageSchema validates with optional fields', () => {
|
|
65
65
|
const validMessage = {
|
|
66
66
|
ts: '1234567890.123456',
|
|
67
67
|
text: 'Hello world',
|
|
@@ -79,7 +79,7 @@ test('SlackMessageSchema validates with optional fields', () => {
|
|
|
79
79
|
expect(() => SlackMessageSchema.parse(validMessage)).not.toThrow()
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
it('SlackMessageSchema validates with files', () => {
|
|
83
83
|
const validMessage = {
|
|
84
84
|
ts: '1234567890.123456',
|
|
85
85
|
text: 'Hello world',
|
|
@@ -102,14 +102,14 @@ test('SlackMessageSchema validates with files', () => {
|
|
|
102
102
|
expect(() => SlackMessageSchema.parse(validMessage)).not.toThrow()
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
it('SlackMessageSchema rejects missing required fields', () => {
|
|
106
106
|
const invalidMessage = {
|
|
107
107
|
ts: '1234567890.123456',
|
|
108
108
|
}
|
|
109
109
|
expect(() => SlackMessageSchema.parse(invalidMessage)).toThrow()
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
it('SlackUserSchema validates correct data', () => {
|
|
113
113
|
const validUser = {
|
|
114
114
|
id: 'U123456',
|
|
115
115
|
name: 'john',
|
|
@@ -122,7 +122,7 @@ test('SlackUserSchema validates correct data', () => {
|
|
|
122
122
|
expect(() => SlackUserSchema.parse(validUser)).not.toThrow()
|
|
123
123
|
})
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
it('SlackUserSchema validates with optional profile', () => {
|
|
126
126
|
const validUser = {
|
|
127
127
|
id: 'U123456',
|
|
128
128
|
name: 'john',
|
|
@@ -141,7 +141,7 @@ test('SlackUserSchema validates with optional profile', () => {
|
|
|
141
141
|
expect(() => SlackUserSchema.parse(validUser)).not.toThrow()
|
|
142
142
|
})
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
it('SlackUserSchema rejects missing required fields', () => {
|
|
145
145
|
const invalidUser = {
|
|
146
146
|
id: 'U123456',
|
|
147
147
|
name: 'john',
|
|
@@ -149,7 +149,7 @@ test('SlackUserSchema rejects missing required fields', () => {
|
|
|
149
149
|
expect(() => SlackUserSchema.parse(invalidUser)).toThrow()
|
|
150
150
|
})
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
it('SlackReactionSchema validates correct data', () => {
|
|
153
153
|
const validReaction = {
|
|
154
154
|
name: 'thumbsup',
|
|
155
155
|
count: 3,
|
|
@@ -158,7 +158,7 @@ test('SlackReactionSchema validates correct data', () => {
|
|
|
158
158
|
expect(() => SlackReactionSchema.parse(validReaction)).not.toThrow()
|
|
159
159
|
})
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
it('SlackReactionSchema rejects invalid data', () => {
|
|
162
162
|
const invalidReaction = {
|
|
163
163
|
name: 'thumbsup',
|
|
164
164
|
count: 'three',
|
|
@@ -167,7 +167,7 @@ test('SlackReactionSchema rejects invalid data', () => {
|
|
|
167
167
|
expect(() => SlackReactionSchema.parse(invalidReaction)).toThrow()
|
|
168
168
|
})
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
it('SlackFileSchema validates correct data', () => {
|
|
171
171
|
const validFile = {
|
|
172
172
|
id: 'F123456',
|
|
173
173
|
name: 'document.pdf',
|
|
@@ -181,7 +181,7 @@ test('SlackFileSchema validates correct data', () => {
|
|
|
181
181
|
expect(() => SlackFileSchema.parse(validFile)).not.toThrow()
|
|
182
182
|
})
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
it('SlackFileSchema validates with optional channels', () => {
|
|
185
185
|
const validFile = {
|
|
186
186
|
id: 'F123456',
|
|
187
187
|
name: 'document.pdf',
|
|
@@ -196,7 +196,7 @@ test('SlackFileSchema validates with optional channels', () => {
|
|
|
196
196
|
expect(() => SlackFileSchema.parse(validFile)).not.toThrow()
|
|
197
197
|
})
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
it('SlackFileSchema rejects missing required fields', () => {
|
|
200
200
|
const invalidFile = {
|
|
201
201
|
id: 'F123456',
|
|
202
202
|
name: 'document.pdf',
|
|
@@ -204,7 +204,7 @@ test('SlackFileSchema rejects missing required fields', () => {
|
|
|
204
204
|
expect(() => SlackFileSchema.parse(invalidFile)).toThrow()
|
|
205
205
|
})
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
it('WorkspaceCredentialsSchema validates correct data', () => {
|
|
208
208
|
const validCredentials = {
|
|
209
209
|
workspace_id: 'T123456',
|
|
210
210
|
workspace_name: 'my-workspace',
|
|
@@ -214,7 +214,7 @@ test('WorkspaceCredentialsSchema validates correct data', () => {
|
|
|
214
214
|
expect(() => WorkspaceCredentialsSchema.parse(validCredentials)).not.toThrow()
|
|
215
215
|
})
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
it('WorkspaceCredentialsSchema rejects missing fields', () => {
|
|
218
218
|
const invalidCredentials = {
|
|
219
219
|
workspace_id: 'T123456',
|
|
220
220
|
workspace_name: 'my-workspace',
|
|
@@ -222,7 +222,7 @@ test('WorkspaceCredentialsSchema rejects missing fields', () => {
|
|
|
222
222
|
expect(() => WorkspaceCredentialsSchema.parse(invalidCredentials)).toThrow()
|
|
223
223
|
})
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
it('ConfigSchema validates correct data', () => {
|
|
226
226
|
const validConfig = {
|
|
227
227
|
current_workspace: 'T123456',
|
|
228
228
|
workspaces: {
|
|
@@ -237,7 +237,7 @@ test('ConfigSchema validates correct data', () => {
|
|
|
237
237
|
expect(() => ConfigSchema.parse(validConfig)).not.toThrow()
|
|
238
238
|
})
|
|
239
239
|
|
|
240
|
-
|
|
240
|
+
it('ConfigSchema validates with null current_workspace', () => {
|
|
241
241
|
const validConfig = {
|
|
242
242
|
current_workspace: null,
|
|
243
243
|
workspaces: {
|
|
@@ -252,7 +252,7 @@ test('ConfigSchema validates with null current_workspace', () => {
|
|
|
252
252
|
expect(() => ConfigSchema.parse(validConfig)).not.toThrow()
|
|
253
253
|
})
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
it('ConfigSchema rejects invalid workspace credentials', () => {
|
|
256
256
|
const invalidConfig = {
|
|
257
257
|
current_workspace: 'T123456',
|
|
258
258
|
workspaces: {
|