fix (security/line): fail closed when webhook auth is missing

This commit is contained in:
Vignesh Natarajan
2026-02-15 19:25:11 -08:00
parent d19b746928
commit beb77229c0
2 changed files with 26 additions and 6 deletions

View File

@@ -119,12 +119,13 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
}, },
}; };
}, },
isConfigured: (account) => Boolean(account.channelAccessToken?.trim()), isConfigured: (account) =>
Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim()),
describeAccount: (account) => ({ describeAccount: (account) => ({
accountId: account.accountId, accountId: account.accountId,
name: account.name, name: account.name,
enabled: account.enabled, enabled: account.enabled,
configured: Boolean(account.channelAccessToken?.trim()), configured: Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim()),
tokenSource: account.tokenSource ?? undefined, tokenSource: account.tokenSource ?? undefined,
}), }),
resolveAllowFrom: ({ cfg, accountId }) => resolveAllowFrom: ({ cfg, accountId }) =>
@@ -603,7 +604,7 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
probeAccount: async ({ account, timeoutMs }) => probeAccount: async ({ account, timeoutMs }) =>
getLineRuntime().channel.line.probeLineBot(account.channelAccessToken, timeoutMs), getLineRuntime().channel.line.probeLineBot(account.channelAccessToken, timeoutMs),
buildAccountSnapshot: ({ account, runtime, probe }) => { buildAccountSnapshot: ({ account, runtime, probe }) => {
const configured = Boolean(account.channelAccessToken?.trim()); const configured = Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim());
return { return {
accountId: account.accountId, accountId: account.accountId,
name: account.name, name: account.name,
@@ -626,6 +627,16 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
const account = ctx.account; const account = ctx.account;
const token = account.channelAccessToken.trim(); const token = account.channelAccessToken.trim();
const secret = account.channelSecret.trim(); const secret = account.channelSecret.trim();
if (!token) {
throw new Error(
`LINE webhook mode requires a non-empty channel access token for account "${account.accountId}".`,
);
}
if (!secret) {
throw new Error(
`LINE webhook mode requires a non-empty channel secret for account "${account.accountId}".`,
);
}
let lineBotLabel = ""; let lineBotLabel = "";
try { try {

View File

@@ -129,6 +129,15 @@ export async function monitorLineProvider(
webhookPath, webhookPath,
} = opts; } = opts;
const resolvedAccountId = accountId ?? "default"; const resolvedAccountId = accountId ?? "default";
const token = channelAccessToken.trim();
const secret = channelSecret.trim();
if (!token) {
throw new Error("LINE webhook mode requires a non-empty channel access token.");
}
if (!secret) {
throw new Error("LINE webhook mode requires a non-empty channel secret.");
}
// Record starting state // Record starting state
recordChannelRuntimeState({ recordChannelRuntimeState({
@@ -142,8 +151,8 @@ export async function monitorLineProvider(
// Create the bot // Create the bot
const bot = createLineBot({ const bot = createLineBot({
channelAccessToken, channelAccessToken: token,
channelSecret, channelSecret: secret,
accountId, accountId,
runtime, runtime,
config, config,
@@ -281,7 +290,7 @@ export async function monitorLineProvider(
pluginId: "line", pluginId: "line",
accountId: resolvedAccountId, accountId: resolvedAccountId,
log: (msg) => logVerbose(msg), log: (msg) => logVerbose(msg),
handler: createLineNodeWebhookHandler({ channelSecret, bot, runtime }), handler: createLineNodeWebhookHandler({ channelSecret: secret, bot, runtime }),
}); });
logVerbose(`line: registered webhook handler at ${normalizedPath}`); logVerbose(`line: registered webhook handler at ${normalizedPath}`);