@open-everywhere/provider-slack 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/LICENSE +201 -0
- package/NOTICE +21 -0
- package/README.md +47 -0
- package/agent/access/slack-attachment-fetch.ts +168 -0
- package/agent/app-manifest.ts +149 -0
- package/agent/channels/slack-approval-card.ts +275 -0
- package/agent/channels/slack-attachments.ts +195 -0
- package/agent/channels/slack-commands.ts +227 -0
- package/agent/channels/slack-context.ts +185 -0
- package/agent/channels/slack-events.ts +5406 -0
- package/agent/channels/slack-front.ts +2349 -0
- package/agent/channels/slack-identity.ts +520 -0
- package/agent/channels/slack-input-response.ts +162 -0
- package/agent/channels/slack-mrkdwn.ts +61 -0
- package/agent/channels/slack-origin-codec.ts +62 -0
- package/agent/channels/slack-post-arm.ts +314 -0
- package/agent/channels/slack-read.ts +248 -0
- package/agent/channels/slack-route-hold.ts +107 -0
- package/agent/channels/slack-shortcut.ts +336 -0
- package/agent/channels/slack-signature.ts +53 -0
- package/agent/channels/slack-spend-gate.ts +62 -0
- package/agent/channels/slack-tenant-fence.ts +99 -0
- package/agent/channels/slack-wake-backscroll.ts +604 -0
- package/agent/channels/slack-web-api.ts +178 -0
- package/agent/channels/slack-webhook-verifier.ts +116 -0
- package/agent/channels/slack.ts +1266 -0
- package/agent/contributions.ts +32 -0
- package/agent/docs-generated.ts +15 -0
- package/agent/ops.ts +50 -0
- package/agent/provider-events.ts +91 -0
- package/agent/register.ts +144 -0
- package/agent/surface-parity.ts +99 -0
- package/dist/agent/access/slack-attachment-fetch.d.ts +15 -0
- package/dist/agent/access/slack-attachment-fetch.d.ts.map +1 -0
- package/dist/agent/access/slack-attachment-fetch.js +151 -0
- package/dist/agent/access/slack-attachment-fetch.js.map +1 -0
- package/dist/agent/app-manifest.d.ts +15 -0
- package/dist/agent/app-manifest.d.ts.map +1 -0
- package/dist/agent/app-manifest.js +141 -0
- package/dist/agent/app-manifest.js.map +1 -0
- package/dist/agent/channels/slack-approval-card.d.ts +71 -0
- package/dist/agent/channels/slack-approval-card.d.ts.map +1 -0
- package/dist/agent/channels/slack-approval-card.js +232 -0
- package/dist/agent/channels/slack-approval-card.js.map +1 -0
- package/dist/agent/channels/slack-attachments.d.ts +66 -0
- package/dist/agent/channels/slack-attachments.d.ts.map +1 -0
- package/dist/agent/channels/slack-attachments.js +174 -0
- package/dist/agent/channels/slack-attachments.js.map +1 -0
- package/dist/agent/channels/slack-commands.d.ts +27 -0
- package/dist/agent/channels/slack-commands.d.ts.map +1 -0
- package/dist/agent/channels/slack-commands.js +208 -0
- package/dist/agent/channels/slack-commands.js.map +1 -0
- package/dist/agent/channels/slack-context.d.ts +26 -0
- package/dist/agent/channels/slack-context.d.ts.map +1 -0
- package/dist/agent/channels/slack-context.js +178 -0
- package/dist/agent/channels/slack-context.js.map +1 -0
- package/dist/agent/channels/slack-events.d.ts +1654 -0
- package/dist/agent/channels/slack-events.d.ts.map +1 -0
- package/dist/agent/channels/slack-events.js +4240 -0
- package/dist/agent/channels/slack-events.js.map +1 -0
- package/dist/agent/channels/slack-front.d.ts +387 -0
- package/dist/agent/channels/slack-front.d.ts.map +1 -0
- package/dist/agent/channels/slack-front.js +1958 -0
- package/dist/agent/channels/slack-front.js.map +1 -0
- package/dist/agent/channels/slack-identity.d.ts +245 -0
- package/dist/agent/channels/slack-identity.d.ts.map +1 -0
- package/dist/agent/channels/slack-identity.js +405 -0
- package/dist/agent/channels/slack-identity.js.map +1 -0
- package/dist/agent/channels/slack-input-response.d.ts +26 -0
- package/dist/agent/channels/slack-input-response.d.ts.map +1 -0
- package/dist/agent/channels/slack-input-response.js +127 -0
- package/dist/agent/channels/slack-input-response.js.map +1 -0
- package/dist/agent/channels/slack-mrkdwn.d.ts +3 -0
- package/dist/agent/channels/slack-mrkdwn.d.ts.map +1 -0
- package/dist/agent/channels/slack-mrkdwn.js +53 -0
- package/dist/agent/channels/slack-mrkdwn.js.map +1 -0
- package/dist/agent/channels/slack-origin-codec.d.ts +7 -0
- package/dist/agent/channels/slack-origin-codec.d.ts.map +1 -0
- package/dist/agent/channels/slack-origin-codec.js +55 -0
- package/dist/agent/channels/slack-origin-codec.js.map +1 -0
- package/dist/agent/channels/slack-post-arm.d.ts +54 -0
- package/dist/agent/channels/slack-post-arm.d.ts.map +1 -0
- package/dist/agent/channels/slack-post-arm.js +268 -0
- package/dist/agent/channels/slack-post-arm.js.map +1 -0
- package/dist/agent/channels/slack-read.d.ts +84 -0
- package/dist/agent/channels/slack-read.d.ts.map +1 -0
- package/dist/agent/channels/slack-read.js +205 -0
- package/dist/agent/channels/slack-read.js.map +1 -0
- package/dist/agent/channels/slack-route-hold.d.ts +34 -0
- package/dist/agent/channels/slack-route-hold.d.ts.map +1 -0
- package/dist/agent/channels/slack-route-hold.js +97 -0
- package/dist/agent/channels/slack-route-hold.js.map +1 -0
- package/dist/agent/channels/slack-shortcut.d.ts +162 -0
- package/dist/agent/channels/slack-shortcut.d.ts.map +1 -0
- package/dist/agent/channels/slack-shortcut.js +262 -0
- package/dist/agent/channels/slack-shortcut.js.map +1 -0
- package/dist/agent/channels/slack-signature.d.ts +19 -0
- package/dist/agent/channels/slack-signature.d.ts.map +1 -0
- package/dist/agent/channels/slack-signature.js +50 -0
- package/dist/agent/channels/slack-signature.js.map +1 -0
- package/dist/agent/channels/slack-spend-gate.d.ts +22 -0
- package/dist/agent/channels/slack-spend-gate.d.ts.map +1 -0
- package/dist/agent/channels/slack-spend-gate.js +48 -0
- package/dist/agent/channels/slack-spend-gate.js.map +1 -0
- package/dist/agent/channels/slack-tenant-fence.d.ts +61 -0
- package/dist/agent/channels/slack-tenant-fence.d.ts.map +1 -0
- package/dist/agent/channels/slack-tenant-fence.js +88 -0
- package/dist/agent/channels/slack-tenant-fence.js.map +1 -0
- package/dist/agent/channels/slack-wake-backscroll.d.ts +188 -0
- package/dist/agent/channels/slack-wake-backscroll.d.ts.map +1 -0
- package/dist/agent/channels/slack-wake-backscroll.js +473 -0
- package/dist/agent/channels/slack-wake-backscroll.js.map +1 -0
- package/dist/agent/channels/slack-web-api.d.ts +46 -0
- package/dist/agent/channels/slack-web-api.d.ts.map +1 -0
- package/dist/agent/channels/slack-web-api.js +147 -0
- package/dist/agent/channels/slack-web-api.js.map +1 -0
- package/dist/agent/channels/slack-webhook-verifier.d.ts +5 -0
- package/dist/agent/channels/slack-webhook-verifier.d.ts.map +1 -0
- package/dist/agent/channels/slack-webhook-verifier.js +112 -0
- package/dist/agent/channels/slack-webhook-verifier.js.map +1 -0
- package/dist/agent/channels/slack.d.ts +37 -0
- package/dist/agent/channels/slack.d.ts.map +1 -0
- package/dist/agent/channels/slack.js +1169 -0
- package/dist/agent/channels/slack.js.map +1 -0
- package/dist/agent/contributions.d.ts +17 -0
- package/dist/agent/contributions.d.ts.map +1 -0
- package/dist/agent/contributions.js +32 -0
- package/dist/agent/contributions.js.map +1 -0
- package/dist/agent/docs-generated.d.ts +3 -0
- package/dist/agent/docs-generated.d.ts.map +1 -0
- package/dist/agent/docs-generated.js +13 -0
- package/dist/agent/docs-generated.js.map +1 -0
- package/dist/agent/ops.d.ts +3 -0
- package/dist/agent/ops.d.ts.map +1 -0
- package/dist/agent/ops.js +38 -0
- package/dist/agent/ops.js.map +1 -0
- package/dist/agent/provider-events.d.ts +3 -0
- package/dist/agent/provider-events.d.ts.map +1 -0
- package/dist/agent/provider-events.js +84 -0
- package/dist/agent/provider-events.js.map +1 -0
- package/dist/agent/register.d.ts +5 -0
- package/dist/agent/register.d.ts.map +1 -0
- package/dist/agent/register.js +118 -0
- package/dist/agent/register.js.map +1 -0
- package/dist/agent/surface-parity.d.ts +3 -0
- package/dist/agent/surface-parity.d.ts.map +1 -0
- package/dist/agent/surface-parity.js +95 -0
- package/dist/agent/surface-parity.js.map +1 -0
- package/package.json +209 -0
- package/tsconfig.build.json +22 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// slack-web-api — THE ONE place a Slack Web API call leaves this host (OPE-1211).
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS MODULE REPLACED eve's `callSlackApi`. Every Slack write used to funnel into
|
|
4
|
+
// `callSlackApi` from `eve/channels/slack`. That primitive is a bare `fetch` POST: it reads no
|
|
5
|
+
// status but the 2xx/non-2xx split, and on a non-2xx it throws a `SlackApiError` carrying
|
|
6
|
+
// `{ method, response, status }` — and NOT the response headers. Slack states a rate-limit delay in
|
|
7
|
+
// the `Retry-After` HEADER and nowhere else, so that delay was unreadable from OE code. Honouring it
|
|
8
|
+
// therefore needs OE to own the request. This module is a faithful port of eve's encoding, token
|
|
9
|
+
// resolution and error posture, with the 429 backoff added around the call.
|
|
10
|
+
//
|
|
11
|
+
// 🔴 WHAT THIS SEAM COVERS, EXACTLY. Every Slack Web API call this repo issues DIRECTLY goes through
|
|
12
|
+
// here: `slackForm`, `callSlackRead`, the `auth.test` identity probes and the wake backscroll. It
|
|
13
|
+
// does NOT cover the calls that ride eve's session BINDING, because those never reach OE code:
|
|
14
|
+
// • `ctx.slack.request(...)` — slack-context.ts (`auth.test`, `conversations.history`);
|
|
15
|
+
// • `ctx.thread.postEphemeral(...)` — slack-input-response.ts;
|
|
16
|
+
// • eve's own machinery — `startTyping`, thread `refresh`, `uploadSlackFiles`.
|
|
17
|
+
// Those resolve through eve's `createSlackRequester`/`postSlackEphemeral` to the same bare
|
|
18
|
+
// `callSlackApi`, so they still fail on the first 429. `slack-web-api-seam-test.ts` holds that list
|
|
19
|
+
// as a KNOWN-UNCOVERED inventory and fails when a NEW bypass appears, which is the honest guarantee:
|
|
20
|
+
// the seam is not universal, but the set of things outside it cannot grow unnoticed.
|
|
21
|
+
//
|
|
22
|
+
// SEMANTICS PORTED VERBATIM from eve's primitive, because callers depend on each one:
|
|
23
|
+
// • Form encoding. Slack's JSON support is partial (`conversations.replies`/`history` want form),
|
|
24
|
+
// so form is the only safe default. A null or undefined value is dropped; a string, number or
|
|
25
|
+
// boolean is stringified; anything else is JSON-encoded.
|
|
26
|
+
// • Token resolution. An explicit token wins, otherwise `SLACK_BOT_TOKEN`. Neither ⇒ throw
|
|
27
|
+
// `SLACK_BOT_TOKEN is required.` — the same message and the same fail-loud posture. ONE
|
|
28
|
+
// DELIBERATE NARROWING: eve's `SlackBotToken` also accepts a function (the secret-manager and
|
|
29
|
+
// rotation form). No caller in this repo passes one, so this seam takes a string. Widening it
|
|
30
|
+
// back is a one-line `await` on the resolver if the multi-bot seam ever needs it.
|
|
31
|
+
// • `ok:false` PASSES THROUGH. Slack answers HTTP 200 with `ok:false` when it read the request and
|
|
32
|
+
// refused it. That is a successful transport, and the callers classify it themselves
|
|
33
|
+
// (slack-read.ts marks it not-applied; slack-post-arm.ts reads it as definitive). This module
|
|
34
|
+
// must never turn it into a throw.
|
|
35
|
+
// • A non-2xx THROWS {@link SlackWebApiError}. A request that got no usable reply stays ambiguous,
|
|
36
|
+
// which is what the post spine needs to avoid double-posting.
|
|
37
|
+
//
|
|
38
|
+
// WHAT IS NEW: a 429 is retried after the delay `Retry-After` names, bounded by
|
|
39
|
+
// `channels/rate-limit-retry.js`. When the attempts run out the call throws `RateLimitExhaustedError`
|
|
40
|
+
// — a NAMED failure. Non-429 responses are untouched.
|
|
41
|
+
import { withRetryAfter } from "@open-everywhere/core/agent/adapter-api.js";
|
|
42
|
+
/** Slack's Web API base. A trailing slash is required — `new URL(operation, base)` resolves the
|
|
43
|
+
* operation against the LAST path segment otherwise. */
|
|
44
|
+
const SLACK_API_BASE = "https://slack.com/api/";
|
|
45
|
+
/** The failure a non-2xx Slack response produces. Mirrors eve's `SlackApiError` field-for-field, so
|
|
46
|
+
* a caller that reads `status` or `response` off a caught error keeps reading the same thing. */
|
|
47
|
+
export class SlackWebApiError extends Error {
|
|
48
|
+
method;
|
|
49
|
+
status;
|
|
50
|
+
response;
|
|
51
|
+
constructor(init) {
|
|
52
|
+
super(`Slack ${init.method} returned HTTP ${init.status}`);
|
|
53
|
+
this.name = "SlackWebApiError";
|
|
54
|
+
this.method = init.method;
|
|
55
|
+
this.status = init.status;
|
|
56
|
+
this.response = init.response;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Form-encode a Slack request body exactly as eve's `encodeSlackApiBody` does. */
|
|
60
|
+
export function encodeSlackForm(body) {
|
|
61
|
+
const params = new URLSearchParams();
|
|
62
|
+
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
63
|
+
for (const [key, value] of Object.entries(body)) {
|
|
64
|
+
if (value === null || value === undefined)
|
|
65
|
+
continue;
|
|
66
|
+
const scalar = typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
67
|
+
params.set(key, scalar ? String(value) : JSON.stringify(value));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return params.toString();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Operations that are BEST-EFFORT PROBES on a latency-critical path, and their tightened policy.
|
|
74
|
+
*
|
|
75
|
+
* 🔴 WHY THIS EXISTS (OPE-1211 review finding 2). `auth.test` resolves the bot's own identity, and
|
|
76
|
+
* `slack-front.ts` awaits it BEFORE returning `OK()` on the route Slack posts events to. Slack times
|
|
77
|
+
* that ack out at 3 seconds and re-delivers; an app that keeps missing the ack gets its event
|
|
78
|
+
* subscription disabled. Under the shared defaults a workspace-wide 429 — the exact case this whole
|
|
79
|
+
* change exists for — would block that ack for up to 60 s and turn a rate-limit episode into a
|
|
80
|
+
* delivery outage. So the probe waits at most about 2 s in total, and its callers already degrade
|
|
81
|
+
* gracefully when it answers nothing.
|
|
82
|
+
*
|
|
83
|
+
* `conversations.info` and `users.info` are here for the same reason (deploy #142 batch review):
|
|
84
|
+
* the Slack front route awaits both before `OK()` — `channelFactsFor` resolves through
|
|
85
|
+
* `channelInternalAndMeta` (fails closed to `{internal:false}`) and `authorFactsFor` through
|
|
86
|
+
* `slackAuthorIsMember` (fails closed to `undefined`) — so the shared 60 s budget would hold the
|
|
87
|
+
* 3-second ack exactly like `auth.test`.
|
|
88
|
+
*
|
|
89
|
+
* `conversations.open` joined them for the SAME two reasons (OPE-1376): a GLOBAL shortcut has no
|
|
90
|
+
* conversation, so the front route awaits this call to resolve the invoker's DM before it acks, and
|
|
91
|
+
* `liveOpenDirectMessageChannel` fails closed to `undefined` (the shortcut then wakes nothing and
|
|
92
|
+
* says so). Slack's interactivity budget is 3 seconds, so the shared policy would hold the ack past
|
|
93
|
+
* it on a rate-limited workspace. It is NOT here for the governance DM-delivery arm, which makes
|
|
94
|
+
* the same call off any ack's critical path and is content with the shared budget.
|
|
95
|
+
*
|
|
96
|
+
* Only add an operation here if BOTH hold: its caller treats a failure as non-fatal, AND something
|
|
97
|
+
* time-critical waits on it.
|
|
98
|
+
*
|
|
99
|
+
* Every member is pinned by name in `scripts/slack-rate-limit-test.ts` § 4c (OPE-1242): drop one
|
|
100
|
+
* and it falls back to the shared 4-attempt / 30 s policy, which that suite asserts against.
|
|
101
|
+
*/
|
|
102
|
+
const PROBE_OPERATIONS = new Set(["auth.test", "conversations.info", "users.info", "conversations.open"]);
|
|
103
|
+
const PROBE_MAX_ATTEMPTS = 2;
|
|
104
|
+
const PROBE_CAP_MS = 2_000;
|
|
105
|
+
/** An explicit token, else `SLACK_BOT_TOKEN`. Throws eve's exact message when neither is set. */
|
|
106
|
+
function resolveBotToken(botToken) {
|
|
107
|
+
const token = botToken ?? process.env.SLACK_BOT_TOKEN;
|
|
108
|
+
if (!token)
|
|
109
|
+
throw new Error("SLACK_BOT_TOKEN is required.");
|
|
110
|
+
return token;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* POST one Slack Web API method, with the 429 backoff applied.
|
|
114
|
+
*
|
|
115
|
+
* Returns Slack's parsed JSON envelope on any 2xx, `ok:false` included. Throws
|
|
116
|
+
* {@link SlackWebApiError} on a non-2xx that is not a 429, and `RateLimitExhaustedError` when a 429
|
|
117
|
+
* outlasts the bounded attempts.
|
|
118
|
+
*/
|
|
119
|
+
export async function callSlackWebApi(input, deps = {}) {
|
|
120
|
+
const token = resolveBotToken(input.botToken);
|
|
121
|
+
const encoded = encodeSlackForm(input.body);
|
|
122
|
+
const url = new URL(input.operation, deps.apiBase ?? SLACK_API_BASE).toString();
|
|
123
|
+
const send = () => (deps.fetch ?? globalThis.fetch)(url, {
|
|
124
|
+
body: encoded,
|
|
125
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/x-www-form-urlencoded" },
|
|
126
|
+
method: "POST",
|
|
127
|
+
});
|
|
128
|
+
const probe = PROBE_OPERATIONS.has(input.operation);
|
|
129
|
+
const res = await withRetryAfter(send, {
|
|
130
|
+
surface: "slack",
|
|
131
|
+
operation: input.operation,
|
|
132
|
+
// An explicit dep always wins, so a test still drives the policy it wants.
|
|
133
|
+
maxAttempts: deps.maxAttempts ?? (probe ? PROBE_MAX_ATTEMPTS : undefined),
|
|
134
|
+
capMs: deps.capMs ?? (probe ? PROBE_CAP_MS : undefined),
|
|
135
|
+
totalBudgetMs: deps.totalBudgetMs,
|
|
136
|
+
sleep: deps.sleep,
|
|
137
|
+
now: deps.now,
|
|
138
|
+
});
|
|
139
|
+
// eve parses the body before the status check, so a non-2xx carries Slack's error envelope. Keep
|
|
140
|
+
// that, and keep the parse failure that a non-JSON body produces — callers already treat any throw
|
|
141
|
+
// from this seam as an ambiguous transport failure.
|
|
142
|
+
const json = (await res.json());
|
|
143
|
+
if (!res.ok)
|
|
144
|
+
throw new SlackWebApiError({ method: input.operation, status: res.status, response: json });
|
|
145
|
+
return json;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=slack-web-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-web-api.js","sourceRoot":"","sources":["../../../agent/channels/slack-web-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type WebhookVerifier } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
/** The registry key — what a generic hook declares as its `source` to be verified as Slack. */
|
|
3
|
+
export declare const SLACK_WEBHOOK_SOURCE = "slack";
|
|
4
|
+
export declare const slackWebhookVerifier: WebhookVerifier;
|
|
5
|
+
//# sourceMappingURL=slack-webhook-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-webhook-verifier.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack-webhook-verifier.ts"],"names":[],"mappings":"AAuDA,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAEzG,+FAA+F;AAC/F,eAAO,MAAM,oBAAoB,UAAU,CAAC;AA0B5C,eAAO,MAAM,oBAAoB,EAAE,eA+BlC,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Slack's webhook signature scheme, as a registry entry (OPE-197) — what lets a GENERIC hook
|
|
2
|
+
// (`POST /webhooks/<id>`) declare `source: "slack"` and be verified exactly the way Slack signs,
|
|
3
|
+
// instead of the deployment being able to check that signature in the surface front door and
|
|
4
|
+
// nowhere else.
|
|
5
|
+
//
|
|
6
|
+
// WHY THIS EXISTS AT ALL, in the two terms that survive. (1) `registeredWebhookVerifierSources()` is
|
|
7
|
+
// printed VERBATIM in the gate's 401 (webhooks/gate.ts), so it is a live statement to a caller about
|
|
8
|
+
// what this deployment can authenticate — and it was under-reporting a scheme the tree has
|
|
9
|
+
// implemented and tested for as long as the Slack surface has existed. (2) Registration is the only
|
|
10
|
+
// place `keyOrigin` and `signedTimestamp` get DECLARED, and those two fields are what drive
|
|
11
|
+
// `acceptsMintedSecret` (webhooks/source-vocabulary.ts) and the durable single-use replay dedupe
|
|
12
|
+
// (webhooks/replay-dedupe.ts). Inline in a front door, both facts are true of the code and invisible
|
|
13
|
+
// to every registry that reasons about them.
|
|
14
|
+
//
|
|
15
|
+
// ZERO NEW CRYPTO. The basestring and digest are `channels/slack-signature.ts`'s — the SAME
|
|
16
|
+
// primitive the surface front door (`verifySlackSignature`) uses, extracted for this entry rather
|
|
17
|
+
// than copied, so there is one implementation of Slack's scheme in the tree and not two that can
|
|
18
|
+
// drift. What this file adds is the part a registry entry needs and a boolean-returning front door
|
|
19
|
+
// has no use for: the closed FAILURE vocabulary, and iteration over a rotation's key list.
|
|
20
|
+
//
|
|
21
|
+
// THE SCHEME (https://api.slack.com/authentication/verifying-requests-from-slack):
|
|
22
|
+
// X-Slack-Request-Timestamp: <unix seconds>
|
|
23
|
+
// X-Slack-Signature: v0=<hex> HMAC-SHA256 of `v0:<timestamp>:<raw body>`.
|
|
24
|
+
//
|
|
25
|
+
// 🔴 `keyOrigin: "vendor-issued"` (OPE-186) — the key is the app's **Signing Secret**, which SLACK
|
|
26
|
+
// generates and shows on the app's Basic Information page (docs/deploy/surfaces/slack.md step 1.5:
|
|
27
|
+
// *"copy the Signing Secret"*). It is read, never chosen, so a secret WE minted can never verify a
|
|
28
|
+
// Slack delivery. That is what keeps `slack` OUT of the chat-mintable source vocabulary
|
|
29
|
+
// (`acceptsMintedSecret` is false for it) — only an OPERATOR-DECLARED hook, whose `secretRef` the
|
|
30
|
+
// operator points at the stored signing secret, can use this verifier. Registering it does NOT make
|
|
31
|
+
// it chat-creatable, and that is the intended outcome, not a limitation to be fixed later.
|
|
32
|
+
//
|
|
33
|
+
// ⭐ `signedTimestamp: true` — and this is the substantive gain over leaving it inline. Slack's
|
|
34
|
+
// basestring COVERS the timestamp, so the freshness claim is the sender's own signed one, and this
|
|
35
|
+
// verifier enforces a window against it. Under `webhooks/gate.ts` that promotes the source from
|
|
36
|
+
// "replay bounded by nothing" to the durable single-use 409 (webhooks/replay-dedupe.ts) — the layer
|
|
37
|
+
// `github` and `notion` structurally cannot have, because they sign the body alone.
|
|
38
|
+
//
|
|
39
|
+
// ⚠️ THE SKEW WINDOW HERE IS THE DEPLOYMENT'S, NOT SLACK'S. The front door hard-codes Slack's own
|
|
40
|
+
// documented 5 minutes because it answers a Slack Request URL; this entry reads
|
|
41
|
+
// `webhookMaxSkewSeconds()` (`OE_WEBHOOK_MAX_SKEW_SECONDS`, default 300) because it answers the
|
|
42
|
+
// GENERIC ingress, where an operator's one freshness bound governs every scheme. The two coincide at
|
|
43
|
+
// the default on purpose and are allowed to diverge on purpose.
|
|
44
|
+
//
|
|
45
|
+
// NOTE this entry changes NO existing route. `/eve/v1/slack` and `/eve/v1/slack/commands` keep their
|
|
46
|
+
// own inline gate, unchanged; registering here only makes the scheme available to the generic
|
|
47
|
+
// ingress.
|
|
48
|
+
import { createHash } from "node:crypto";
|
|
49
|
+
import { SLACK_SIGNATURE_HEADER, SLACK_SIGNATURE_VERSION, SLACK_TIMESTAMP_HEADER, slackSignatureMatches, } from "./slack-signature.js";
|
|
50
|
+
import { webhookMaxSkewSeconds } from "@open-everywhere/core/agent/adapter-api.js";
|
|
51
|
+
/** The registry key — what a generic hook declares as its `source` to be verified as Slack. */
|
|
52
|
+
export const SLACK_WEBHOOK_SOURCE = "slack";
|
|
53
|
+
/** Unix seconds, as Slack sends them. Checked as TEXT before `Number()` so `"1e9"`, `" 12"` and
|
|
54
|
+
* `"12.0"` are `malformed-timestamp` rather than silently coerced into a passing window. */
|
|
55
|
+
const TIMESTAMP_RE = /^[0-9]{1,15}$/;
|
|
56
|
+
/**
|
|
57
|
+
* The opaque single-use nonce for an accepted Slack delivery.
|
|
58
|
+
*
|
|
59
|
+
* 🔴 IT IS THE SIGNED CONTENT, HASHED — not a delivery id, because Slack's scheme has none: the
|
|
60
|
+
* basestring is `v0:<timestamp>:<rawBody>` and nothing else, so `(timestamp, rawBody)` IS the
|
|
61
|
+
* complete identity the sender signed. Two presentations agreeing on both are, to this scheme,
|
|
62
|
+
* indistinguishable — which is exactly what "the same delivery, twice" means here.
|
|
63
|
+
*
|
|
64
|
+
* WHAT THAT COSTS, stated rather than assumed: a byte-identical re-send from Slack inside the window
|
|
65
|
+
* is graded a replay. That is deliberate and it is nearly free, because a byte-identical re-send
|
|
66
|
+
* OUTSIDE the window already fails the skew check at 401 — so this dedupe only ever changes the
|
|
67
|
+
* verdict in the interval where the scheme itself cannot tell a legitimate re-send from a captured
|
|
68
|
+
* one. Deriving it from the SIGNATURE instead would have been wrong for a different reason: the
|
|
69
|
+
* signature is key-dependent, so it would change identity across a rotation overlap for a delivery
|
|
70
|
+
* that never changed.
|
|
71
|
+
*/
|
|
72
|
+
function slackReplayKey(timestamp, rawBody) {
|
|
73
|
+
return createHash("sha256").update(`${SLACK_WEBHOOK_SOURCE}:${timestamp}:${rawBody}`).digest("hex");
|
|
74
|
+
}
|
|
75
|
+
export const slackWebhookVerifier = {
|
|
76
|
+
source: SLACK_WEBHOOK_SOURCE,
|
|
77
|
+
// Provider-owned ⇒ routed through admitProviderRegistration: de-select the slack bundle and this
|
|
78
|
+
// row never lands, so a `source: "slack"` hook 401s instead of silently using another scheme.
|
|
79
|
+
provider: "slack",
|
|
80
|
+
keyKind: "shared-secret",
|
|
81
|
+
keyOrigin: "vendor-issued",
|
|
82
|
+
signedTimestamp: true,
|
|
83
|
+
verify({ rawBody, headers, keys, nowMs }) {
|
|
84
|
+
const signature = headers.get(SLACK_SIGNATURE_HEADER);
|
|
85
|
+
if (!signature)
|
|
86
|
+
return { ok: false, failure: "missing-signature" };
|
|
87
|
+
const timestamp = headers.get(SLACK_TIMESTAMP_HEADER);
|
|
88
|
+
// A missing timestamp is its own failure, not `missing-signature`: it is COVERED BY the
|
|
89
|
+
// signature, so without it there is no basestring to compute at all.
|
|
90
|
+
if (timestamp === null)
|
|
91
|
+
return { ok: false, failure: "missing-timestamp" };
|
|
92
|
+
if (!TIMESTAMP_RE.test(timestamp))
|
|
93
|
+
return { ok: false, failure: "malformed-timestamp" };
|
|
94
|
+
// SKEW BEFORE ANY HMAC — a stale replay must not cost a digest per configured key.
|
|
95
|
+
if (Math.abs(nowMs / 1000 - Number(timestamp)) > webhookMaxSkewSeconds())
|
|
96
|
+
return { ok: false, failure: "stale-timestamp" };
|
|
97
|
+
// A signature tagged with a version we do not implement is `unsupported-scheme`, never an
|
|
98
|
+
// attempted match: if Slack ever ships a `v1`, guessing at it is the shape of a downgrade.
|
|
99
|
+
if (!signature.startsWith(`${SLACK_SIGNATURE_VERSION}=`))
|
|
100
|
+
return { ok: false, failure: "unsupported-scheme" };
|
|
101
|
+
for (const [i, key] of keys.entries()) {
|
|
102
|
+
// Ordered, current-first: a match at i>0 is a rotation overlap and the gate audits it. Slack
|
|
103
|
+
// documents no overlap window of its own (rotating the signing secret is immediate), so
|
|
104
|
+
// `keys[]` is how an operator gets one on our side.
|
|
105
|
+
if (slackSignatureMatches(key, timestamp, rawBody, signature)) {
|
|
106
|
+
return { ok: true, keyIndex: i, replayKey: slackReplayKey(timestamp, rawBody) };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { ok: false, failure: "signature-mismatch" };
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=slack-webhook-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-webhook-verifier.js","sourceRoot":"","sources":["../../../agent/channels/slack-webhook-verifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type SlackContext, type SlackMessage } from "eve/channels/slack";
|
|
2
|
+
import { type OpenEverywhereSlackAuth } from "./slack-identity.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `chat.postMessage` form body for a channel-supervisor's host-side reply post (OPE-878).
|
|
5
|
+
*
|
|
6
|
+
* `deliverSupervisorReply`'s `post` callback bypasses the post-arm registry (slack-post-arm.ts) —
|
|
7
|
+
* it builds its own `slackForm` call rather than going through `dispatchSlackReply` — so it needs
|
|
8
|
+
* its OWN `markdownToSlackMrkdwn` conversion at the wire; that seam does not cover it. This is that
|
|
9
|
+
* ONE conversion point, extracted so a test can drive it without a live Slack call (the closure
|
|
10
|
+
* above cannot be invoked directly — it captures live session state).
|
|
11
|
+
*/
|
|
12
|
+
export declare function supervisorPostFormBody(channelId: string, threadTs: string | undefined, text: string): string;
|
|
13
|
+
/** What eve's mention/DM hook may return. `null` (={@link DROP}) is the ONLY value that
|
|
14
|
+
* dispatches no model turn — see {@link DROP}. */
|
|
15
|
+
type InboundOutcome = {
|
|
16
|
+
readonly auth: OpenEverywhereSlackAuth | null;
|
|
17
|
+
readonly context?: readonly string[];
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* THE WAKE-ADMISSION CHOKEPOINT for eve's native mention/DM door (OPE-33) — the only thing wired
|
|
21
|
+
* into `onAppMention`/`onDirectMessage`.
|
|
22
|
+
*
|
|
23
|
+
* A returned turn is a spend commitment, so this wrapper enforces the invariant that inspection
|
|
24
|
+
* kept failing to hold: **nothing is admitted unless the spend gate was consulted for THIS
|
|
25
|
+
* message**. Fail-CLOSED — an admission with no consult (a future early-return added above the
|
|
26
|
+
* gate, exactly the shape that broke it) is downgraded to a real DROP and said out loud, rather
|
|
27
|
+
* than quietly spending. A DROP passes straight through (nothing to gate: no turn, no spend).
|
|
28
|
+
*
|
|
29
|
+
* The refusal itself is posted by the gate, so a declined wake is still visible in-thread; this
|
|
30
|
+
* wrapper only decides whether a MODEL TURN follows.
|
|
31
|
+
*/
|
|
32
|
+
export declare function admitInbound(ctx: SlackContext, message: SlackMessage, isDm: boolean, run?: (consult: {
|
|
33
|
+
gated: boolean;
|
|
34
|
+
}) => Promise<InboundOutcome>): Promise<InboundOutcome>;
|
|
35
|
+
declare const _default: import("eve/channels/slack").SlackChannel;
|
|
36
|
+
export default _default;
|
|
37
|
+
//# sourceMappingURL=slack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,YAAY,EAGjB,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAQL,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC;AA4K7B;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI5G;AA+QD;mDACmD;AACnD,KAAK,cAAc,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAAC;AA4OrH;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,OAAO,EACb,GAAG,GAAE,CAAC,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,cAAc,CAAuD,GAClH,OAAO,CAAC,cAAc,CAAC,CAYzB"}
|