Skip to content

fix(slack): validate Slack ID prefix matches declared target kind #2087

@alexey-pelykh

Description

@alexey-pelykh

Summary

parseSlackTarget in src/slack/targets.ts blindly trusts the user-provided prefix (user:, channel:) without validating that the Slack ID format matches the declared kind. Slack IDs have well-known single-letter prefixes that indicate their type:

ID prefix Slack type Valid target kind
U User user
W Workspace user (Enterprise Grid) user
B Bot user user
C Public channel channel
G Private channel/group channel
D DM channel channel

Currently user:D0AKUMPAKMF is accepted and parsed as kind: "user", but D-prefixed IDs are DM channels. This silently misconfigures delivery targets and causes downstream failures (e.g., cron announce delivery fails with no indication of the misconfiguration).

Reproduction

  1. Configure a cron job with --to "user:D0AKUMPAKMF" (DM channel ID with user: prefix)
  2. Job runs, agent produces output
  3. Announce delivery fails — no clear error about the ID/kind mismatch
  4. Fix: change to --to "channel:D0AKUMPAKMF" → works immediately

Proposed Fix

In parseSlackTarget (src/slack/targets.ts), after parsing a prefixed target, validate the ID's first character against the declared kind:

const SLACK_USER_ID_PREFIXES = /^[UWB]/i;
const SLACK_CHANNEL_ID_PREFIXES = /^[CGD]/i;

// After parseTargetPrefixes returns a result:
if (prefixedTarget) {
  const idChar = prefixedTarget.id[0]?.toUpperCase();
  if (prefixedTarget.kind === "user" && SLACK_CHANNEL_ID_PREFIXES.test(idChar)) {
    throw new Error(
      `Slack ID "${prefixedTarget.id}" looks like a ${idChar === "D" ? "DM channel" : "channel"} (${idChar}-prefix), ` +
      `but was specified as user:. Use channel:${prefixedTarget.id} instead.`
    );
  }
  if (prefixedTarget.kind === "channel" && SLACK_USER_ID_PREFIXES.test(idChar)) {
    throw new Error(
      `Slack ID "${prefixedTarget.id}" looks like a user (${idChar}-prefix), ` +
      `but was specified as channel:. Use user:${prefixedTarget.id} instead.`
    );
  }
  return prefixedTarget;
}

Scope

  • Only Slack — Discord uses numeric snowflake IDs with no type-prefix convention
  • Validation at parse time in parseSlackTarget, not in the generic channels/targets.ts
  • Error with a helpful message (suggest the correct prefix), don't silently auto-correct

Discovered While

Debugging cron announce delivery failure (#2083). The root issue was auth (#2083), but after fixing auth, delivery still failed because the Slack target used user:D... instead of channel:D....

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions