Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions src/main/__tests__/integration-event-bridge.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import assert from 'node:assert/strict'
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { beforeEach, test } from 'node:test'

Expand Down Expand Up @@ -873,6 +875,159 @@ test('slack raw-id event resolves context through mounted slug alias', async ()
assert.match(harness.sent[1].input.text, /Message:\nedited Slack message/u)
})

test('slack raw-id event falls back to matched local suffixed mount when remote read misses', async () => {
const tempRoot = await mkdtemp(join(tmpdir(), 'pear-slack-local-context-'))
const localRoot = join(tempRoot, 'workspace-id', 'slack', 'channels', 'C123ABC__proj-cloud')
const remotePath = '/slack/channels/C123ABC/messages/1780668000_000000/meta.json'
const localRemotePath = '/slack/channels/C123ABC__proj-cloud/messages/1780668000_000000/meta.json'

try {
await mkdir(join(localRoot, 'messages', '1780668000_000000'), { recursive: true })
await writeFile(
join(localRoot, 'messages', '1780668000_000000', 'meta.json'),
JSON.stringify({ provider: 'slack', text: 'local mounted Slack message' })
)
const harness = makeHarness(['alice'], {
failReadFile: true
})

await withMockedNow('2026-06-05T14:00:00.000Z', async () => {
await harness.bridge.reconcile('project-1', [
integration({
provider: 'slack',
integrationId: 'slack-1',
mountPaths: ['/slack/channels/C123ABC__proj-cloud'],
localMountPaths: [localRoot],
downloadHistoricalData: false,
scope: { notifyAgents: ['alice'] }
})
])
})

await harness.emit({
...changeEvent(
remotePath,
'slack',
{ digest: 'revision:raw-copy' }
),
expand: async () => ({
level: 'full',
path: remotePath,
data: {
path: remotePath,
deleted: false
}
})
} as ChangeEvent)
await waitForSent(harness, 1, 2_500)

assert.match(harness.sent[0].input.text, /Message:\nlocal mounted Slack message/u)
assert.match(harness.sent[0].input.text, /Path: \.integrations\/slack\/channels\/C123ABC__proj-cloud\/messages\/1780668000_000000\/meta\.json/u)
assert.equal(harness.sent[0].input.data?.path, localRemotePath)
assert.deepEqual(
(harness.sent[0].input.data?.resource as { path?: string } | undefined)?.path,
localRemotePath
)
assert.equal((harness.sent[0].input.data?.contextPreview as { path?: string } | undefined)?.path, localRemotePath)
assert.deepEqual(harness.readFileCalls.slice(0, 2), [
{
workspaceId: 'workspace-id',
path: remotePath
},
{
workspaceId: 'workspace-id',
path: localRemotePath
}
])

await harness.emit(changeEvent(
localRemotePath,
'slack',
{ digest: 'revision:slug-copy' }
))
await waitForDropped('project-1', 1, 2_500)

assert.equal(harness.sent.length, 1)
assert.deepEqual(harness.readFileCalls.slice(8, 10), [
{
workspaceId: 'workspace-id',
path: localRemotePath
},
{
workspaceId: 'workspace-id',
path: remotePath
}
])
} finally {
await rm(tempRoot, { recursive: true, force: true })
}
})

test('slack local context fallback rejects traversal outside matched mount root', async () => {
const tempRoot = await mkdtemp(join(tmpdir(), 'pear-slack-local-traversal-'))
const localRoot = join(tempRoot, 'workspace-id', 'slack', 'channels', 'C123ABC__proj-cloud')
const escapedRoot = join(tempRoot, 'workspace-id', 'slack', 'leaked')
const remotePath = '/slack/channels/C123ABC/messages/1780668000_000000/../../../../leaked/meta.json'
const localRemotePath = '/slack/channels/C123ABC__proj-cloud/messages/1780668000_000000/../../../../leaked/meta.json'

try {
await mkdir(escapedRoot, { recursive: true })
await writeFile(
join(escapedRoot, 'meta.json'),
JSON.stringify({ provider: 'slack', text: 'escaped Slack message' })
)
const harness = makeHarness(['alice'], {
failReadFile: true
})

await withMockedNow('2026-06-05T14:00:00.000Z', async () => {
await harness.bridge.reconcile('project-1', [
integration({
provider: 'slack',
integrationId: 'slack-1',
mountPaths: ['/slack/channels/C123ABC__proj-cloud'],
localMountPaths: [localRoot],
downloadHistoricalData: false,
scope: { notifyAgents: ['alice'] }
})
])
})

await harness.emit({
...changeEvent(
remotePath,
'slack',
{ digest: 'revision:traversal-copy' }
),
expand: async () => ({
level: 'full',
path: remotePath,
data: {
path: remotePath,
deleted: false
}
})
} as ChangeEvent)
await waitForSent(harness, 1, 2_500)

assert.doesNotMatch(harness.sent[0].input.text, /escaped Slack message/u)
assert.match(harness.sent[0].input.text, /Message: unavailable/u)
assert.equal(harness.sent[0].input.data?.contextPreview, undefined)
assert.deepEqual(harness.readFileCalls.slice(0, 2), [
{
workspaceId: 'workspace-id',
path: remotePath
},
{
workspaceId: 'workspace-id',
path: localRemotePath
}
])
} finally {
await rm(tempRoot, { recursive: true, force: true })
}
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

test('slack unchanged-content replay re-drives after injected delivery is not confirmed', async () => {
const options = { failInjected: true }
const harness = makeHarness(['alice'], options)
Expand Down
Loading
Loading