agent-messenger 2.1.0 → 2.3.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/.claude-plugin/plugin.json +1 -1
- package/.env.template +35 -17
- package/README.md +7 -7
- package/bun.lock +31 -7
- package/dist/package.json +5 -3
- package/dist/src/platforms/channeltalk/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/commands/auth.js +35 -28
- package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/ensure-auth.js +6 -6
- package/dist/src/platforms/channeltalk/ensure-auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts +23 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.js +299 -29
- package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
- package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/auth.js +57 -49
- package/dist/src/platforms/discord/commands/auth.js.map +1 -1
- package/dist/src/platforms/discord/ensure-auth.js +3 -3
- package/dist/src/platforms/discord/ensure-auth.js.map +1 -1
- package/dist/src/platforms/discord/token-extractor.d.ts +6 -1
- package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/discord/token-extractor.js +167 -14
- package/dist/src/platforms/discord/token-extractor.js.map +1 -1
- package/dist/src/platforms/instagram/client.d.ts +2 -0
- package/dist/src/platforms/instagram/client.d.ts.map +1 -1
- package/dist/src/platforms/instagram/client.js +2 -2
- package/dist/src/platforms/instagram/client.js.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.js +107 -14
- package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
- package/dist/src/platforms/instagram/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/instagram/ensure-auth.js +57 -11
- package/dist/src/platforms/instagram/ensure-auth.js.map +1 -1
- package/dist/src/platforms/instagram/index.d.ts +1 -0
- package/dist/src/platforms/instagram/index.d.ts.map +1 -1
- package/dist/src/platforms/instagram/index.js +1 -0
- package/dist/src/platforms/instagram/index.js.map +1 -1
- package/dist/src/platforms/instagram/token-extractor.d.ts +44 -0
- package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/instagram/token-extractor.js +407 -0
- package/dist/src/platforms/instagram/token-extractor.js.map +1 -0
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +2 -1
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/auth.js +14 -13
- package/dist/src/platforms/kakaotalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
- package/dist/src/platforms/line/client.d.ts.map +1 -1
- package/dist/src/platforms/line/client.js +36 -9
- package/dist/src/platforms/line/client.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +6 -5
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/auth.js +11 -10
- package/dist/src/platforms/slack/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/token-extractor.d.ts +9 -0
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/slack/token-extractor.js +300 -23
- package/dist/src/platforms/slack/token-extractor.js.map +1 -1
- package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/auth.js +9 -8
- package/dist/src/platforms/teams/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.js +2 -1
- package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts +5 -0
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js +161 -29
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/platforms/telegram/client.d.ts.map +1 -1
- package/dist/src/platforms/telegram/client.js +25 -7
- package/dist/src/platforms/telegram/client.js.map +1 -1
- package/dist/src/platforms/telegram/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/telegram/commands/auth.js +6 -5
- package/dist/src/platforms/telegram/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts +12 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +168 -1
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webex/commands/auth.d.ts +4 -0
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/auth.js +50 -4
- package/dist/src/platforms/webex/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/credential-manager.js +1 -1
- package/dist/src/platforms/webex/credential-manager.js.map +1 -1
- package/dist/src/platforms/webex/encryption.d.ts +10 -0
- package/dist/src/platforms/webex/encryption.d.ts.map +1 -0
- package/dist/src/platforms/webex/encryption.js +49 -0
- package/dist/src/platforms/webex/encryption.js.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.js +25 -5
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
- package/dist/src/platforms/webex/index.d.ts +2 -0
- package/dist/src/platforms/webex/index.d.ts.map +1 -1
- package/dist/src/platforms/webex/index.js +1 -0
- package/dist/src/platforms/webex/index.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts +29 -0
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/webex/token-extractor.js +393 -0
- package/dist/src/platforms/webex/token-extractor.js.map +1 -0
- package/dist/src/platforms/webex/types.d.ts +8 -1
- package/dist/src/platforms/webex/types.d.ts.map +1 -1
- package/dist/src/platforms/webex/types.js +4 -1
- package/dist/src/platforms/webex/types.js.map +1 -1
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +6 -2
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/shared/utils/derived-key-cache.d.ts +1 -1
- package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
- package/dist/src/shared/utils/error-handler.d.ts +1 -1
- package/dist/src/shared/utils/error-handler.d.ts.map +1 -1
- package/dist/src/shared/utils/error-handler.js +3 -2
- package/dist/src/shared/utils/error-handler.js.map +1 -1
- package/dist/src/shared/utils/stderr.d.ts +5 -0
- package/dist/src/shared/utils/stderr.d.ts.map +1 -0
- package/dist/src/shared/utils/stderr.js +18 -0
- package/dist/src/shared/utils/stderr.js.map +1 -0
- package/docs/content/docs/cli/channeltalk.mdx +7 -7
- package/docs/content/docs/cli/discord.mdx +3 -3
- package/docs/content/docs/cli/instagram.mdx +28 -6
- package/docs/content/docs/cli/slack.mdx +2 -2
- package/docs/content/docs/cli/teams.mdx +6 -4
- package/docs/content/docs/cli/webex.mdx +32 -11
- package/e2e/README.md +132 -8
- package/e2e/channeltalk.e2e.test.ts +2 -7
- package/e2e/channeltalkbot.e2e.test.ts +2 -6
- package/e2e/config.ts +172 -10
- package/e2e/helpers.ts +7 -0
- package/e2e/instagram.e2e.test.ts +97 -0
- package/e2e/kakaotalk.e2e.test.ts +74 -0
- package/e2e/line.e2e.test.ts +92 -0
- package/e2e/teams.e2e.test.ts +46 -1
- package/e2e/telegram.e2e.test.ts +84 -0
- package/e2e/webex.e2e.test.ts +190 -0
- package/e2e/whatsapp.e2e.test.ts +90 -0
- package/e2e/whatsappbot.e2e.test.ts +78 -0
- package/package.json +5 -3
- package/skills/agent-channeltalk/SKILL.md +9 -9
- package/skills/agent-channeltalk/references/authentication.md +21 -18
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +5 -5
- package/skills/agent-discord/references/authentication.md +8 -8
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +51 -9
- package/skills/agent-instagram/references/authentication.md +35 -3
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +5 -5
- package/skills/agent-slack/references/authentication.md +8 -8
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +6 -6
- package/skills/agent-teams/references/authentication.md +8 -8
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +35 -15
- package/skills/agent-webex/references/authentication.md +63 -9
- package/skills/agent-webex/references/common-patterns.md +6 -3
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/channeltalk/commands/auth.test.ts +5 -5
- package/src/platforms/channeltalk/commands/auth.ts +38 -32
- package/src/platforms/channeltalk/ensure-auth.test.ts +6 -6
- package/src/platforms/channeltalk/ensure-auth.ts +6 -6
- package/src/platforms/channeltalk/token-extractor.test.ts +182 -15
- package/src/platforms/channeltalk/token-extractor.ts +344 -30
- package/src/platforms/discord/commands/auth.test.ts +3 -3
- package/src/platforms/discord/commands/auth.ts +58 -54
- package/src/platforms/discord/ensure-auth.test.ts +3 -3
- package/src/platforms/discord/ensure-auth.ts +3 -3
- package/src/platforms/discord/token-extractor.test.ts +199 -27
- package/src/platforms/discord/token-extractor.ts +190 -17
- package/src/platforms/instagram/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.ts +133 -14
- package/src/platforms/instagram/ensure-auth.ts +63 -12
- package/src/platforms/instagram/index.ts +1 -0
- package/src/platforms/instagram/token-extractor.test.ts +424 -0
- package/src/platforms/instagram/token-extractor.ts +478 -0
- package/src/platforms/kakaotalk/client.ts +3 -1
- package/src/platforms/kakaotalk/commands/auth.ts +14 -13
- package/src/platforms/kakaotalk/protocol/connection.ts +3 -1
- package/src/platforms/line/client.ts +39 -14
- package/src/platforms/line/commands/auth.ts +7 -6
- package/src/platforms/slack/cli.test.ts +6 -5
- package/src/platforms/slack/commands/auth.test.ts +11 -7
- package/src/platforms/slack/commands/auth.ts +11 -10
- package/src/platforms/slack/token-extractor.test.ts +98 -1
- package/src/platforms/slack/token-extractor.ts +338 -26
- package/src/platforms/teams/commands/auth.ts +9 -8
- package/src/platforms/teams/ensure-auth.ts +3 -1
- package/src/platforms/teams/token-extractor.test.ts +136 -17
- package/src/platforms/teams/token-extractor.ts +182 -31
- package/src/platforms/telegram/client.test.ts +134 -0
- package/src/platforms/telegram/client.ts +27 -6
- package/src/platforms/telegram/commands/auth.ts +6 -5
- package/src/platforms/webex/client.test.ts +314 -0
- package/src/platforms/webex/client.ts +231 -1
- package/src/platforms/webex/commands/auth.ts +71 -4
- package/src/platforms/webex/commands/member.test.ts +10 -1
- package/src/platforms/webex/commands/message.test.ts +9 -5
- package/src/platforms/webex/commands/snapshot.test.ts +13 -4
- package/src/platforms/webex/commands/space.test.ts +12 -2
- package/src/platforms/webex/credential-manager.ts +1 -1
- package/src/platforms/webex/encryption.ts +53 -0
- package/src/platforms/webex/ensure-auth.test.ts +4 -0
- package/src/platforms/webex/ensure-auth.ts +27 -4
- package/src/platforms/webex/index.ts +2 -0
- package/src/platforms/webex/token-extractor.test.ts +327 -0
- package/src/platforms/webex/token-extractor.ts +460 -0
- package/src/platforms/webex/types.ts +8 -2
- package/src/platforms/webex/typings/node-jose.d.ts +27 -0
- package/src/platforms/whatsapp/client.ts +11 -7
- package/src/shared/utils/derived-key-cache.ts +1 -1
- package/src/shared/utils/error-handler.ts +4 -2
- package/src/shared/utils/stderr.ts +22 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, spyOn, test } from 'bun:test'
|
|
2
|
+
import { homedir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
import { InstagramTokenExtractor } from './token-extractor'
|
|
6
|
+
|
|
7
|
+
describe('InstagramTokenExtractor', () => {
|
|
8
|
+
let extractor: InstagramTokenExtractor
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
extractor = new InstagramTokenExtractor()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
describe('getBrowserCookiesPaths', () => {
|
|
15
|
+
test('returns darwin paths for all browsers on macOS', () => {
|
|
16
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
17
|
+
const paths = darwinExtractor.getBrowserCookiesPaths()
|
|
18
|
+
|
|
19
|
+
const base = join(homedir(), 'Library', 'Application Support')
|
|
20
|
+
expect(paths).toContain(join(base, 'Google', 'Chrome', 'Default', 'Cookies'))
|
|
21
|
+
expect(paths).toContain(join(base, 'Google', 'Chrome', 'Default', 'Network', 'Cookies'))
|
|
22
|
+
expect(paths).toContain(join(base, 'Microsoft Edge', 'Default', 'Cookies'))
|
|
23
|
+
expect(paths).toContain(join(base, 'Arc', 'User Data', 'Default', 'Cookies'))
|
|
24
|
+
expect(paths).toContain(join(base, 'BraveSoftware', 'Brave-Browser', 'Default', 'Cookies'))
|
|
25
|
+
expect(paths).toContain(join(base, 'Vivaldi', 'Default', 'Cookies'))
|
|
26
|
+
expect(paths).toContain(join(base, 'Chromium', 'Default', 'Cookies'))
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('returns linux paths for supported browsers', () => {
|
|
30
|
+
const linuxExtractor = new InstagramTokenExtractor('linux')
|
|
31
|
+
const paths = linuxExtractor.getBrowserCookiesPaths()
|
|
32
|
+
|
|
33
|
+
const base = join(homedir(), '.config')
|
|
34
|
+
expect(paths).toContain(join(base, 'google-chrome', 'Default', 'Cookies'))
|
|
35
|
+
expect(paths).toContain(join(base, 'microsoft-edge', 'Default', 'Cookies'))
|
|
36
|
+
expect(paths).toContain(join(base, 'BraveSoftware', 'Brave-Browser', 'Default', 'Cookies'))
|
|
37
|
+
expect(paths).toContain(join(base, 'vivaldi', 'Default', 'Cookies'))
|
|
38
|
+
expect(paths).toContain(join(base, 'chromium', 'Default', 'Cookies'))
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('returns win32 paths for all browsers on Windows', () => {
|
|
42
|
+
const winExtractor = new InstagramTokenExtractor('win32')
|
|
43
|
+
const paths = winExtractor.getBrowserCookiesPaths()
|
|
44
|
+
|
|
45
|
+
const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
46
|
+
expect(paths).toContain(join(localAppData, 'Google', 'Chrome', 'User Data', 'Default', 'Cookies'))
|
|
47
|
+
expect(paths).toContain(join(localAppData, 'Microsoft', 'Edge', 'User Data', 'Default', 'Cookies'))
|
|
48
|
+
expect(paths).toContain(join(localAppData, 'BraveSoftware', 'Brave-Browser', 'User Data', 'Default', 'Cookies'))
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('returns empty array for unsupported platform', () => {
|
|
52
|
+
const unsupportedExtractor = new InstagramTokenExtractor('freebsd' as NodeJS.Platform)
|
|
53
|
+
const paths = unsupportedExtractor.getBrowserCookiesPaths()
|
|
54
|
+
expect(paths).toEqual([])
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('includes Network/Cookies variant paths', () => {
|
|
58
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
59
|
+
const paths = darwinExtractor.getBrowserCookiesPaths()
|
|
60
|
+
|
|
61
|
+
const base = join(homedir(), 'Library', 'Application Support')
|
|
62
|
+
expect(paths).toContain(join(base, 'Google', 'Chrome', 'Default', 'Network', 'Cookies'))
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('getLocalStatePaths', () => {
|
|
67
|
+
test('returns darwin Local State paths for all browsers', () => {
|
|
68
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
69
|
+
const paths = darwinExtractor.getLocalStatePaths()
|
|
70
|
+
|
|
71
|
+
const base = join(homedir(), 'Library', 'Application Support')
|
|
72
|
+
expect(paths).toContain(join(base, 'Google', 'Chrome', 'Local State'))
|
|
73
|
+
expect(paths).toContain(join(base, 'Microsoft Edge', 'Local State'))
|
|
74
|
+
expect(paths).toContain(join(base, 'BraveSoftware', 'Brave-Browser', 'Local State'))
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('returns linux Local State paths for supported browsers', () => {
|
|
78
|
+
const linuxExtractor = new InstagramTokenExtractor('linux')
|
|
79
|
+
const paths = linuxExtractor.getLocalStatePaths()
|
|
80
|
+
|
|
81
|
+
const base = join(homedir(), '.config')
|
|
82
|
+
expect(paths).toContain(join(base, 'google-chrome', 'Local State'))
|
|
83
|
+
expect(paths).toContain(join(base, 'microsoft-edge', 'Local State'))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('returns win32 Local State paths for all browsers', () => {
|
|
87
|
+
const winExtractor = new InstagramTokenExtractor('win32')
|
|
88
|
+
const paths = winExtractor.getLocalStatePaths()
|
|
89
|
+
|
|
90
|
+
const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
91
|
+
expect(paths).toContain(join(localAppData, 'Google', 'Chrome', 'User Data', 'Local State'))
|
|
92
|
+
expect(paths).toContain(join(localAppData, 'Microsoft', 'Edge', 'User Data', 'Local State'))
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('getKeychainVariants', () => {
|
|
97
|
+
test('returns all Chromium browser keychain variants', () => {
|
|
98
|
+
expect(extractor.getKeychainVariants()).toEqual([
|
|
99
|
+
{ service: 'Chrome Safe Storage', account: 'Chrome' },
|
|
100
|
+
{ service: 'Chrome Canary Safe Storage', account: 'Chrome Canary' },
|
|
101
|
+
{ service: 'Microsoft Edge Safe Storage', account: 'Microsoft Edge' },
|
|
102
|
+
{ service: 'Arc Safe Storage', account: 'Arc' },
|
|
103
|
+
{ service: 'Brave Safe Storage', account: 'Brave' },
|
|
104
|
+
{ service: 'Vivaldi Safe Storage', account: 'Vivaldi' },
|
|
105
|
+
{ service: 'Chromium Safe Storage', account: 'Chromium' },
|
|
106
|
+
])
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('isEncryptedValue', () => {
|
|
111
|
+
test('detects v10 encrypted values', () => {
|
|
112
|
+
const encrypted = Buffer.from('v10encrypted_data')
|
|
113
|
+
expect(extractor.isEncryptedValue(encrypted)).toBe(true)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('detects v11 encrypted values', () => {
|
|
117
|
+
const encrypted = Buffer.from('v11encrypted_data')
|
|
118
|
+
expect(extractor.isEncryptedValue(encrypted)).toBe(true)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('rejects non-encrypted values', () => {
|
|
122
|
+
const plain = Buffer.from('plain_text')
|
|
123
|
+
expect(extractor.isEncryptedValue(plain)).toBe(false)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('rejects empty buffers', () => {
|
|
127
|
+
const empty = Buffer.alloc(0)
|
|
128
|
+
expect(extractor.isEncryptedValue(empty)).toBe(false)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('rejects short buffers under 4 bytes', () => {
|
|
132
|
+
const short = Buffer.from('v1')
|
|
133
|
+
expect(extractor.isEncryptedValue(short)).toBe(false)
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
describe('isValidSessionId', () => {
|
|
138
|
+
test('accepts session IDs of 20 or more chars', () => {
|
|
139
|
+
const valid = 'abcdefghijklmnopqrstu'
|
|
140
|
+
expect(extractor.isValidSessionId(valid)).toBe(true)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('accepts long session IDs', () => {
|
|
144
|
+
const valid = 'a'.repeat(100)
|
|
145
|
+
expect(extractor.isValidSessionId(valid)).toBe(true)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('rejects session IDs shorter than 20 chars', () => {
|
|
149
|
+
expect(extractor.isValidSessionId('short')).toBe(false)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('rejects empty string', () => {
|
|
153
|
+
expect(extractor.isValidSessionId('')).toBe(false)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test('rejects null and undefined', () => {
|
|
157
|
+
expect(extractor.isValidSessionId(null as unknown as string)).toBe(false)
|
|
158
|
+
expect(extractor.isValidSessionId(undefined as unknown as string)).toBe(false)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
describe('extract', () => {
|
|
163
|
+
test('returns empty array when no cookie paths exist', async () => {
|
|
164
|
+
const linuxExtractor = new InstagramTokenExtractor('linux')
|
|
165
|
+
const spy = spyOn(linuxExtractor as any, 'copyAndExtract').mockResolvedValue(null)
|
|
166
|
+
|
|
167
|
+
const result = await linuxExtractor.extract()
|
|
168
|
+
expect(result).toEqual([])
|
|
169
|
+
|
|
170
|
+
spy.mockRestore()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('returns extracted cookies when found', async () => {
|
|
174
|
+
const mockCookies = {
|
|
175
|
+
sessionid: 'a'.repeat(25),
|
|
176
|
+
ds_user_id: '12345678',
|
|
177
|
+
csrftoken: 'abc123',
|
|
178
|
+
mid: 'some_mid_value',
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const linuxExtractor = new InstagramTokenExtractor('linux')
|
|
182
|
+
const getBrowserCookiesPathsSpy = spyOn(linuxExtractor, 'getBrowserCookiesPaths').mockReturnValue([
|
|
183
|
+
'/fake/path/Cookies',
|
|
184
|
+
])
|
|
185
|
+
const existsSyncSpy = spyOn(
|
|
186
|
+
await import('node:fs'),
|
|
187
|
+
'existsSync',
|
|
188
|
+
).mockReturnValue(true)
|
|
189
|
+
const copyAndExtractSpy = spyOn(linuxExtractor as any, 'copyAndExtract').mockResolvedValue(mockCookies)
|
|
190
|
+
|
|
191
|
+
const result = await linuxExtractor.extract()
|
|
192
|
+
expect(result).toEqual([mockCookies])
|
|
193
|
+
|
|
194
|
+
getBrowserCookiesPathsSpy.mockRestore()
|
|
195
|
+
existsSyncSpy.mockRestore()
|
|
196
|
+
copyAndExtractSpy.mockRestore()
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
test('tries next path when first fails', async () => {
|
|
200
|
+
const mockCookies = {
|
|
201
|
+
sessionid: 'a'.repeat(25),
|
|
202
|
+
ds_user_id: '12345678',
|
|
203
|
+
csrftoken: 'abc123',
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
207
|
+
const getBrowserCookiesPathsSpy = spyOn(darwinExtractor, 'getBrowserCookiesPaths').mockReturnValue([
|
|
208
|
+
'/fake/path1/Cookies',
|
|
209
|
+
'/fake/path2/Cookies',
|
|
210
|
+
])
|
|
211
|
+
const existsSyncSpy = spyOn(
|
|
212
|
+
await import('node:fs'),
|
|
213
|
+
'existsSync',
|
|
214
|
+
).mockReturnValue(true)
|
|
215
|
+
const copyAndExtractSpy = spyOn(darwinExtractor as any, 'copyAndExtract')
|
|
216
|
+
.mockResolvedValueOnce(null)
|
|
217
|
+
.mockResolvedValueOnce(mockCookies)
|
|
218
|
+
|
|
219
|
+
const result = await darwinExtractor.extract()
|
|
220
|
+
expect(copyAndExtractSpy).toHaveBeenCalledTimes(2)
|
|
221
|
+
expect(result).toEqual([mockCookies])
|
|
222
|
+
|
|
223
|
+
getBrowserCookiesPathsSpy.mockRestore()
|
|
224
|
+
existsSyncSpy.mockRestore()
|
|
225
|
+
copyAndExtractSpy.mockRestore()
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('returns multiple entries when different ds_user_id values found across profiles', async () => {
|
|
229
|
+
const mockCookies1 = {
|
|
230
|
+
sessionid: 'a'.repeat(25),
|
|
231
|
+
ds_user_id: '11111111',
|
|
232
|
+
csrftoken: 'token1',
|
|
233
|
+
}
|
|
234
|
+
const mockCookies2 = {
|
|
235
|
+
sessionid: 'b'.repeat(25),
|
|
236
|
+
ds_user_id: '22222222',
|
|
237
|
+
csrftoken: 'token2',
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const linuxExtractor = new InstagramTokenExtractor('linux')
|
|
241
|
+
const getBrowserCookiesPathsSpy = spyOn(linuxExtractor, 'getBrowserCookiesPaths').mockReturnValue([
|
|
242
|
+
'/fake/profile1/Cookies',
|
|
243
|
+
'/fake/profile2/Cookies',
|
|
244
|
+
])
|
|
245
|
+
const existsSyncSpy = spyOn(
|
|
246
|
+
await import('node:fs'),
|
|
247
|
+
'existsSync',
|
|
248
|
+
).mockReturnValue(true)
|
|
249
|
+
const copyAndExtractSpy = spyOn(linuxExtractor as any, 'copyAndExtract')
|
|
250
|
+
.mockResolvedValueOnce(mockCookies1)
|
|
251
|
+
.mockResolvedValueOnce(mockCookies2)
|
|
252
|
+
|
|
253
|
+
const result = await linuxExtractor.extract()
|
|
254
|
+
expect(result).toHaveLength(2)
|
|
255
|
+
expect(result[0]?.ds_user_id).toBe('11111111')
|
|
256
|
+
expect(result[1]?.ds_user_id).toBe('22222222')
|
|
257
|
+
|
|
258
|
+
getBrowserCookiesPathsSpy.mockRestore()
|
|
259
|
+
existsSyncSpy.mockRestore()
|
|
260
|
+
copyAndExtractSpy.mockRestore()
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
test('deduplicates entries with the same ds_user_id across profiles', async () => {
|
|
264
|
+
const mockCookies = {
|
|
265
|
+
sessionid: 'a'.repeat(25),
|
|
266
|
+
ds_user_id: '12345678',
|
|
267
|
+
csrftoken: 'abc123',
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const linuxExtractor = new InstagramTokenExtractor('linux')
|
|
271
|
+
const getBrowserCookiesPathsSpy = spyOn(linuxExtractor, 'getBrowserCookiesPaths').mockReturnValue([
|
|
272
|
+
'/fake/profile1/Cookies',
|
|
273
|
+
'/fake/profile2/Cookies',
|
|
274
|
+
])
|
|
275
|
+
const existsSyncSpy = spyOn(
|
|
276
|
+
await import('node:fs'),
|
|
277
|
+
'existsSync',
|
|
278
|
+
).mockReturnValue(true)
|
|
279
|
+
const copyAndExtractSpy = spyOn(linuxExtractor as any, 'copyAndExtract').mockResolvedValue(mockCookies)
|
|
280
|
+
|
|
281
|
+
const result = await linuxExtractor.extract()
|
|
282
|
+
expect(result).toHaveLength(1)
|
|
283
|
+
expect(result[0]?.ds_user_id).toBe('12345678')
|
|
284
|
+
|
|
285
|
+
getBrowserCookiesPathsSpy.mockRestore()
|
|
286
|
+
existsSyncSpy.mockRestore()
|
|
287
|
+
copyAndExtractSpy.mockRestore()
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
describe('copyAndExtract', () => {
|
|
292
|
+
test('returns null when copy fails due to locked database', async () => {
|
|
293
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
294
|
+
const copyFileSpy = spyOn(darwinExtractor as any, 'copyDatabaseToTemp').mockImplementation(() => {
|
|
295
|
+
throw new Error('EBUSY: resource busy or locked')
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
const result = await (darwinExtractor as any).copyAndExtract('/path/to/Cookies')
|
|
299
|
+
expect(result).toBeNull()
|
|
300
|
+
|
|
301
|
+
copyFileSpy.mockRestore()
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
test('cleans up temp file after extraction', async () => {
|
|
305
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
306
|
+
const copyFileSpy = spyOn(darwinExtractor as any, 'copyDatabaseToTemp').mockReturnValue('/tmp/test-cookies')
|
|
307
|
+
const extractSpy = spyOn(darwinExtractor as any, 'extractFromSQLite').mockResolvedValue(null)
|
|
308
|
+
const cleanupSpy = spyOn(darwinExtractor as any, 'cleanupTempFile').mockImplementation(() => {})
|
|
309
|
+
|
|
310
|
+
await (darwinExtractor as any).copyAndExtract('/path/to/Cookies')
|
|
311
|
+
|
|
312
|
+
expect(copyFileSpy).toHaveBeenCalled()
|
|
313
|
+
expect(cleanupSpy).toHaveBeenCalled()
|
|
314
|
+
|
|
315
|
+
copyFileSpy.mockRestore()
|
|
316
|
+
extractSpy.mockRestore()
|
|
317
|
+
cleanupSpy.mockRestore()
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
describe('decryption', () => {
|
|
322
|
+
describe('decryptAESGCM', () => {
|
|
323
|
+
test('returns null for data too short for AES-GCM', () => {
|
|
324
|
+
const invalidData = Buffer.from('too_short')
|
|
325
|
+
const key = Buffer.alloc(32, 0)
|
|
326
|
+
|
|
327
|
+
const result = (extractor as any).decryptAESGCM(invalidData, key)
|
|
328
|
+
expect(result).toBeNull()
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
test('returns null when AES-GCM decryption fails with wrong key', () => {
|
|
332
|
+
const fakeEncrypted = Buffer.concat([
|
|
333
|
+
Buffer.from('v10'),
|
|
334
|
+
Buffer.alloc(12, 1),
|
|
335
|
+
Buffer.alloc(20, 2),
|
|
336
|
+
Buffer.alloc(16, 3),
|
|
337
|
+
])
|
|
338
|
+
const key = Buffer.alloc(32, 0)
|
|
339
|
+
|
|
340
|
+
const result = (extractor as any).decryptAESGCM(fakeEncrypted, key)
|
|
341
|
+
expect(result).toBeNull()
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
describe('decryptAESCBC', () => {
|
|
346
|
+
test('strips 32-byte non-printable integrity hash prefix (Chromium v130+)', () => {
|
|
347
|
+
// given — AES-128-CBC encrypted buffer where decrypted result has a 32-byte non-printable prefix
|
|
348
|
+
const { createCipheriv, pbkdf2Sync } = require('node:crypto')
|
|
349
|
+
const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
|
|
350
|
+
const iv = Buffer.alloc(16, 0x20)
|
|
351
|
+
const actualValue = 'test-session-id-value-for-hash-stripping'
|
|
352
|
+
const prefix = Buffer.alloc(32, 0x01) // 32 bytes of non-printable (0x01 < 0x20)
|
|
353
|
+
const plaintext = Buffer.concat([prefix, Buffer.from(actualValue)])
|
|
354
|
+
|
|
355
|
+
const cipher = createCipheriv('aes-128-cbc', key, iv)
|
|
356
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()])
|
|
357
|
+
const encrypted = Buffer.concat([Buffer.from('v10'), ciphertext])
|
|
358
|
+
|
|
359
|
+
// when
|
|
360
|
+
const result = (extractor as any).decryptAESCBC(encrypted, key)
|
|
361
|
+
|
|
362
|
+
// then
|
|
363
|
+
expect(result).toBe(actualValue)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
test('returns plaintext as-is when no non-printable prefix detected', () => {
|
|
367
|
+
// given — AES-128-CBC encrypted buffer without integrity hash prefix
|
|
368
|
+
const { createCipheriv, pbkdf2Sync } = require('node:crypto')
|
|
369
|
+
const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
|
|
370
|
+
const iv = Buffer.alloc(16, 0x20)
|
|
371
|
+
const plainValue = 'normal-cookie-value-no-prefix'
|
|
372
|
+
|
|
373
|
+
const cipher = createCipheriv('aes-128-cbc', key, iv)
|
|
374
|
+
const ciphertext = Buffer.concat([cipher.update(plainValue, 'utf8'), cipher.final()])
|
|
375
|
+
const encrypted = Buffer.concat([Buffer.from('v10'), ciphertext])
|
|
376
|
+
|
|
377
|
+
// when
|
|
378
|
+
const result = (extractor as any).decryptAESCBC(encrypted, key)
|
|
379
|
+
|
|
380
|
+
// then
|
|
381
|
+
expect(result).toBe(plainValue)
|
|
382
|
+
})
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
describe('getKeychainPassword on macOS', () => {
|
|
386
|
+
test('tries multiple keychain variants until one succeeds', () => {
|
|
387
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
388
|
+
const execSyncSpy = spyOn(darwinExtractor as any, 'execSecurityCommand')
|
|
389
|
+
.mockReturnValueOnce(null)
|
|
390
|
+
.mockReturnValueOnce('test_password')
|
|
391
|
+
|
|
392
|
+
const result = (darwinExtractor as any).getKeychainPassword()
|
|
393
|
+
|
|
394
|
+
expect(execSyncSpy).toHaveBeenCalledTimes(2)
|
|
395
|
+
expect(result).toBe('test_password')
|
|
396
|
+
|
|
397
|
+
execSyncSpy.mockRestore()
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
test('returns null when all keychain variants fail', () => {
|
|
401
|
+
const darwinExtractor = new InstagramTokenExtractor('darwin')
|
|
402
|
+
const execSyncSpy = spyOn(darwinExtractor as any, 'execSecurityCommand').mockReturnValue(null)
|
|
403
|
+
|
|
404
|
+
const result = (darwinExtractor as any).getKeychainPassword()
|
|
405
|
+
|
|
406
|
+
expect(result).toBeNull()
|
|
407
|
+
|
|
408
|
+
execSyncSpy.mockRestore()
|
|
409
|
+
})
|
|
410
|
+
})
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
describe('SQLite extraction', () => {
|
|
414
|
+
test('returns null when database path does not exist', async () => {
|
|
415
|
+
const result = await (extractor as any).extractFromSQLite('/nonexistent/path', '/nonexistent/path')
|
|
416
|
+
expect(result).toBeNull()
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
test('returns null when extraction throws', async () => {
|
|
420
|
+
const result = await (extractor as any).extractFromSQLite('/dev/null', '/dev/null')
|
|
421
|
+
expect(result).toBeNull()
|
|
422
|
+
})
|
|
423
|
+
})
|
|
424
|
+
})
|