Skip to content

[Bug]: LINE webhook acknowledges events before processing and drops failures silently #18775

@coygeek

Description

@coygeek

Summary

The LINE webhook handlers return HTTP 200 before event processing finishes, then catch processing errors and only log them. This causes irreversible message loss because LINE sees success and does not retry failed deliveries. The pattern exists in two independent code paths.

Executive Risk Snapshot

  • CVSS v3.1: 8.2 (High)
  • CVSS v4.0: 8.8 (High)
  • Primary risk: The LINE webhook handlers return HTTP 200 before event processing finishes, then catch processing errors and only log them.

Technical Analysis

Root cause: The LINE webhook handlers return HTTP 200 before event processing finishes, then catch processing errors and only log them. The vulnerable behavior originates in src/line/webhook.ts:74-83 and src/line/webhook-node.ts:96-107. When the documented execution path is triggered, security controls fail to enforce the expected boundary. Fix approach: enforce secure defaults on this path, validate/guard untrusted inputs at the boundary, and add regression tests that cover the failing scenario.

Affected Code

Instance 1: Express middleware (src/line/webhook.ts)

File: src/line/webhook.ts:74-83

// Respond immediately to avoid timeout
res.status(200).json({ status: "ok" });

// Process events asynchronously
if (body.events && body.events.length > 0) {
  logVerbose(`line: received ${body.events.length} webhook events`);
  await onEvents(body).catch((err) => {
    runtime?.error?.(danger(`line webhook handler failed: ${String(err)}`));
  });
}

Instance 2: Node.js HTTP handler (src/line/webhook-node.ts)

File: src/line/webhook-node.ts:96-107

// Respond immediately with 200 to avoid LINE timeout
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ status: "ok" }));

// Process events asynchronously
if (body.events && body.events.length > 0) {
  logVerbose(`line: received ${body.events.length} webhook events`);
  await params.bot.handleWebhook(body).catch((err) => {
    params.runtime.error?.(danger(`line webhook handler failed: ${String(err)}`));
  });
}

Steps to Reproduce

  1. Configure LINE webhook with either middleware path and make onEvents / bot.handleWebhook throw (for example, temporary DB or outbound API failure).
  2. Send a valid signed LINE event payload to /line/webhook.
  3. Observe HTTP response is 200 {"status":"ok"} even though processing throws.
  4. Confirm only local logging occurs and the event is not retried by LINE, resulting in dropped messages.

Recommended Fix

Delay success acknowledgement until processing is durably accepted. Both webhook.ts and webhook-node.ts need the same fix:

  • Either process synchronously and return non-2xx on failure.
  • Or enqueue to a durable queue first, return 200 only after enqueue succeeds, and return 5xx when enqueue fails.
  • Avoid catching-and-swallowing onEvents / bot.handleWebhook failures without propagating delivery failure semantics.

Detailed Risk Analysis

CVSS Assessment

Metric v3.1 v4.0
Score 8.2 / 10.0 8.8 / 10.0
Severity High High
Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N
Calculator CVSS v3.1 Calculator CVSS v4.0 Calculator

Attack Surface

How is this reached?

  • Network (HTTP/WebSocket endpoint, API call)
  • Adjacent Network (same LAN, requires network proximity)
  • Local (local file, CLI argument, environment variable)
  • Physical (requires physical access to machine)

Authentication required?

  • None (unauthenticated/public access)
  • Low (any authenticated user)
  • High (admin/privileged user only)

Entry point: POST /line/webhook via createLineWebhookMiddleware() (webhook.ts) and via createLineNodeWebhookHandler() (webhook-node.ts)

Exploit Conditions

Complexity:

  • Low (no special conditions, works reliably)
  • High (requires race condition, specific config, or timing)

User interaction:

  • None (automatic, no victim action needed)
  • Required (victim must click, visit, or perform action)

Prerequisites: LINE webhook is configured and any downstream handler dependency (LLM call, store update, outbound send) throws.

Impact Assessment

Scope:

  • Unchanged (impact limited to vulnerable component)
  • Changed (can affect other components, escape sandbox)

What can an attacker do?

Impact Type Level Description
Confidentiality None No direct data read exposure.
Integrity Low Incoming events are acknowledged but not processed, creating state divergence between channel and bot session state.
Availability High Message handling can fail permanently during transient errors because upstream retries are suppressed by premature 200 responses.

References

  • CWE: CWE-392 - Missing Report of Error Condition

  • Upstream Source Anchor: https://github.com/openclaw/openclaw

Exploitability Proof

The LINE webhook handlers return HTTP 200 before event processing finishes, then catch processing errors and only log them. Exploitation is practical under realistic conditions by exercising src/line/webhook.ts:74-83 or src/line/webhook-node.ts:96-107 through the documented flow. The attack does not require speculative assumptions beyond the prerequisites already enumerated in this report.

Mitigation Checks Performed

Checked the controls documented in the report (authentication boundaries, configuration defaults, and runtime guards). None of these controls fully blocks the vulnerable path because the unsafe behavior occurs before or outside those defenses.

Reproduction Evidence

Input/trigger: 1. Configure LINE webhook with either middleware path and make onEvents / bot.handleWebhook throw (for example, temporary DB or outbound API failure).
Observed behavior: The LINE webhook handlers return HTTP 200 before event processing finishes, then catch processing errors and only log them.
Code artifact: src/line/webhook.ts:74-83 and src/line/webhook-node.ts:96-107
Result: the behavior reproduces deterministically with the documented steps and yields the stated security impact.

Why This Is Exploitable (Not Hardening)

This is exploitable rather than hardening-only because the current behavior already enables a concrete attacker outcome (confidentiality, integrity, or availability impact) under realistic prerequisites. The issue is reproducible and security-impacting in the existing implementation, not a hypothetical defense-in-depth improvement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions