You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
MaxKB's webhook trigger endpoint at /api/trigger/v1/webhook/{trigger_id} is accessible without any authentication. The WebhookAuth authentication class unconditionally returns (None, {}), effectively disabling all authentication for this endpoint. While the application includes an optional token-based verification mechanism in EventTrigger.execute(), this mechanism is optional — it only activates when the trigger_setting.token field is present and non-empty.
The backend serializer (TriggerValidationMixin._validate_event_setting()) does not enforce that a token must be present when creating an EVENT trigger. The token field is not part of the validation schema for event triggers. Although the Vue frontend auto-generates a UUIDv4-based token when creating event triggers via the UI, the API allows creating event triggers without any token.
An attacker who knows or guesses a trigger ID can invoke any untokened webhook trigger to execute its associated tasks — including running custom Python tool code via ToolExecutor.exec_code() which calls exec() in a subprocess.
WebhookAuth.authenticate() unconditionally returns (None, {}) without checking any credentials. In Django REST Framework, returning a tuple from authenticate() signals successful authentication. This means every request to an endpoint using WebhookAuth is treated as authenticated.
@staticmethoddefexecute(trigger, request=None, **kwargs):
trigger_setting=trigger.get('trigger_setting')
iftrigger_setting.get('token'):
token=request.META.get('HTTP_AUTHORIZATION')
ifnottokenortrigger_setting.get('token') !=token.replace('Bearer ', ''):
raiseAppAuthenticationFailed(1002, _('Authentication information is incorrect'))
The token check is gated by if trigger_setting.get('token'). If token is None, missing, or empty string (""), the entire verification block is skipped. The trigger proceeds to execute its tasks without any authorization check.
3. Backend Does Not Enforce Token Requirement
File:apps/trigger/serializers/trigger.py:241-247
@staticmethoddef_validate_event_setting(setting):
body=setting.get('body')
ifbodyisnotNoneandnotisinstance(body, list):
raiseserializers.ValidationError({
'trigger_setting': _('body must be an array')
})
_validate_event_setting() only validates that body (if present) is an array. There is no validation requiring the token field for EVENT triggers. The token field is completely absent from this validation method.
4. Model Default Has No Token
File:apps/trigger/models/trigger.py:36
trigger_setting=models.JSONField(default=dict)
The default value for trigger_setting is an empty dictionary {}, which contains no token key.
The webhook endpoint is publicly accessible (no workspace_id in URL, no permission decorators).
Attack Chain
graph TD
A[Attacker discovers trigger ID] --> B[POST /api/trigger/v1/webhook/trigger_id]
B --> C[WebhookAuth.authenticate returns None, empty dict]
C --> D[trigger_setting.get token returns None]
D --> E[Token check skipped]
E --> F{Trigger active?}
F -->|Yes| G[EventTrigger.execute runs tasks]
G --> H{Task type?}
H -->|TOOL| I[ToolTask.execute called]
I --> J[ToolExecutor.exec_code executes user Python code]
J --> K[exec in subprocess - code execution]
H -->|APPLICATION| L[ApplicationTask.execute runs workflow]
L --> M[Workflow may contain tool nodes with exec]
F -->|No| N[Returns 404 - no impact]
Loading
Detailed Data Flow
Attacker sendsPOST /api/trigger/v1/webhook/{trigger_id} with arbitrary JSON body
get_parameters() processes the request body according to the trigger's body configuration
simple_tools.execute() iterates over active trigger tasks and dispatches them
For TOOL tasks: ToolTask.execute() → ToolExecutor.exec_code() → exec() in a subprocess
For APPLICATION tasks: ApplicationTask.execute() → runs the application workflow, which may contain tool nodes that call exec()
Key Enabling Factors
Factor
Status
Webhook endpoint has no authentication
Confirmed
Token verification is optional in trigger execution
Confirmed
Backend does not require token for EVENT triggers
Confirmed
Tool code execution via exec() is reachable from triggers
Confirmed
No rate limiting on webhook endpoint
Confirmed
Trigger IDs are UUIDv7 (time-ordered, not cryptographically random)
Confirmed
Webhook URL contains no workspace identifier
Confirmed
Impact Assessment
Direct Impact
Unauthorized Trigger Invocation: Any unauthenticated user can invoke webhook triggers, causing them to execute their configured tasks (tool executions, application workflows).
Denial of Service: Repeated invocation of triggers with expensive operations (LLM calls, tool executions) can exhaust API credits, consume server resources, and degrade service availability.
Information Disclosure: If a trigger task involves an application workflow, the attacker can interact with the knowledge base and obtain AI-generated responses that may contain sensitive information.
Code Execution (conditional): If a trigger is bound to a custom Python tool, the attacker can trigger execution of that tool's code via ToolExecutor.exec_code(). While this executes in a subprocess with optional sandbox restrictions, the code runs with the server's user privileges (sandbox is disabled by default — SANDBOX config defaults to 0).
Preconditions
The attacker must know or discover a valid trigger ID (UUIDv7, ~122 bits entropy — not trivially brute-forceable)
The trigger must be of type EVENT
The trigger must be active (is_active = True)
The trigger's trigger_setting must lack a token field (possible via API-only creation or migration)
HTTP 200
{
"code": 1002,
"data": null,
"message": "Authentication information is incorrect"
}
CVSS v3.1 Scoring
Metric
Value
Rationale
Attack Vector
Network (N)
Exploitable over the network via HTTP POST
Attack Complexity
Low (L)
No special conditions; simple HTTP request
Privileges Required
None (N)
No authentication required
User Interaction
None (N)
No victim interaction needed
Scope
Unchanged (U)
Impact contained within the MaxKB application
Confidentiality
None (N)
No direct data exfiltration (indirect via LLM responses)
Integrity
High (H)
Can execute arbitrary tool code, modify application state
Availability
None (N)
DoS is possible but not the primary impact
Base Score: 7.5 (High) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
Note on Integrity: If the sandbox is disabled (default), ToolExecutor.exec_code() runs user Python code with server-level privileges, enabling file manipulation, network access, and lateral movement within the infrastructure. Even with sandbox enabled, the tool can still perform actions within its permitted scope (e.g., LLM API calls, database operations via the application workflow).
Adjusted Score (with sandbox disabled): 9.8 (Critical) — If code execution is reachable, this becomes a full RCE. However, we rate conservatively at 7.5 because: (a) trigger ID discovery is non-trivial, (b) the default frontend generates tokens, and (c) sandbox may be enabled in production deployments.
Fix Recommendations
1. Make Token Mandatory for EVENT Triggers (Critical)
@staticmethoddef_validate_event_setting(setting):
body=setting.get('body')
ifbodyisnotNoneandnotisinstance(body, list):
raiseserializers.ValidationError({
'trigger_setting': _('body must be an array')
})
# NEW: Require token for EVENT triggersifnotsetting.get('token'):
raiseserializers.ValidationError({
'trigger_setting': _('token is required for EVENT triggers')
})
iflen(str(setting.get('token', ''))) <16:
raiseserializers.ValidationError({
'trigger_setting': _('token must be at least 16 characters')
})
2. Migrate Existing Triggers Without Tokens
Add a data migration that generates tokens for all existing EVENT triggers that lack one:
# Generated token should be communicated to the trigger ownerfromdjango.dbimportmigrationsimportuuiddefgenerate_tokens(apps, schema_editor):
Trigger=apps.get_model('trigger', 'Trigger')
fortriggerinTrigger.objects.filter(trigger_type='EVENT'):
setting=trigger.trigger_settingor {}
ifnotsetting.get('token'):
setting['token'] =uuid.uuid4().hextrigger.trigger_setting=settingtrigger.save(update_fields=['trigger_setting'])
3. Remove WebhookAuth or Make It Functional
Either remove WebhookAuth entirely and rely solely on per-trigger token verification, or implement proper authentication:
classWebhookAuth(TokenAuthentication):
keyword="Bearer"defauthenticate(self, request):
# Return None (not authenticated) by default# The view will still handle token verification per-triggerreturnNone
Important: In DRF, returning None from authenticate() means "no authentication attempted" — the request proceeds with request.user = None and request.auth = None. The current code returns (None, {}) which is incorrect — it signals successful authentication. This should be changed to return None (not a tuple).
Consider adding workspace validation to the webhook trigger execution to prevent cross-workspace trigger invocation:
# In EventTrigger.execute():ifrequestandhasattr(request, 'workspace_id'):
iftrigger.get('workspace_id') !=request.workspace_id:
raiseAppAuthenticationFailed(1002, 'Invalid trigger for this workspace')
Impact: Unauthorized code execution (conditional), unauthorized application workflow invocation, denial of service
AI Cross-Review Notes
Verification Results
Question
Finding
WebhookAuth does no authentication?
Confirmed. Returns (None, {}) which DRF interprets as successful auth. No middleware supplements this.
Trigger ID predictable?
Partially. UUIDv7 is time-ordered (~48 bits timestamp), but has ~122 bits total entropy. Not brute-forceable without leaked IDs.
Can exec() be reached from webhook?
Confirmed. ToolTask.execute() → ToolExecutor.exec_code() → exec() in subprocess. Sandbox is disabled by default.
Token optional by default?
Confirmed. Backend _validate_event_setting() does not require token. Model default is empty dict. Frontend auto-generates token but API does not enforce it.
Can normal users create triggers?
Restricted. Requires WORKSPACE_MANAGE role (admin-level). But any workspace admin can create untokened triggers.
Rate limiting?
None. No throttle classes on the webhook endpoint.
Design choice or vulnerability?
Vulnerability. The intent is clearly optional auth (token in trigger_setting), but making it optional AND having the backend not enforce it AND having the auth class return "authenticated" creates a bypass.
Key Mitigating Factor
The primary mitigating factor is trigger ID discovery. UUIDv7 provides ~122 bits of entropy, making blind enumeration infeasible. However:
Trigger IDs may be leaked via logs, shared URLs, error messages, or API responses
The trigger_task API returns trigger IDs to authenticated users
UUIDv7's time-ordering means knowing the creation time reduces entropy
Third-party integrations may expose trigger IDs in their configuration
Developer Likely Pushback Points
"The frontend always generates a token" — True for UI-created triggers, but the API does not enforce this
"Trigger IDs are UUIDs, not guessable" — True for blind attacks, but IDs can be leaked
"This is by design for webhook integrations" — Valid for the webhook pattern, but should require token by default
"Sandbox limits code execution impact" — Sandbox is disabled by default (SANDBOX=0)
Three-Question Filter
Can this be triggered by an unauthenticated remote attacker? Yes — WebhookAuth returns authenticated for all requests, and token verification is optional.
Is there a complete attack chain from network input to impactful action? Yes — webhook → no auth → trigger execution → tool code exec (conditional on trigger configuration).
Is this a known issue or configuration-dependent? This is a code-level vulnerability, not a configuration issue. The code explicitly makes authentication optional.
Verdict: Confirmed vulnerability. Recommended for disclosure with severity High.
MaxKB Webhook Trigger Authentication Bypass
Vulnerability Type: Improper Authentication / Missing Authorization
Severity: High
CVSS v3.1: 7.5 (High) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
Affected Component: Webhook event trigger endpoint
Affected Versions: MaxKB <= latest (as of 2026-05-04, commit-based analysis)
Discoverer: icysun icysun@qq.com
Status: Pending Disclosure
Overview
MaxKB's webhook trigger endpoint at
/api/trigger/v1/webhook/{trigger_id}is accessible without any authentication. TheWebhookAuthauthentication class unconditionally returns(None, {}), effectively disabling all authentication for this endpoint. While the application includes an optional token-based verification mechanism inEventTrigger.execute(), this mechanism is optional — it only activates when thetrigger_setting.tokenfield is present and non-empty.The backend serializer (
TriggerValidationMixin._validate_event_setting()) does not enforce that a token must be present when creating an EVENT trigger. Thetokenfield is not part of the validation schema for event triggers. Although the Vue frontend auto-generates a UUIDv4-based token when creating event triggers via the UI, the API allows creating event triggers without any token.An attacker who knows or guesses a trigger ID can invoke any untokened webhook trigger to execute its associated tasks — including running custom Python tool code via
ToolExecutor.exec_code()which callsexec()in a subprocess.Root Cause Analysis
1. WebhookAuth — No Authentication Performed
File:
apps/common/auth/authenticate.py:152-157WebhookAuth.authenticate()unconditionally returns(None, {})without checking any credentials. In Django REST Framework, returning a tuple fromauthenticate()signals successful authentication. This means every request to an endpoint usingWebhookAuthis treated as authenticated.2. Token Verification is Optional
File:
apps/trigger/handler/impl/trigger/event_trigger.py:108-112The token check is gated by
if trigger_setting.get('token'). IftokenisNone, missing, or empty string (""), the entire verification block is skipped. The trigger proceeds to execute its tasks without any authorization check.3. Backend Does Not Enforce Token Requirement
File:
apps/trigger/serializers/trigger.py:241-247_validate_event_setting()only validates thatbody(if present) is an array. There is no validation requiring thetokenfield for EVENT triggers. Thetokenfield is completely absent from this validation method.4. Model Default Has No Token
File:
apps/trigger/models/trigger.py:36The default value for
trigger_settingis an empty dictionary{}, which contains notokenkey.5. Webhook Endpoint Uses WebhookAuth
File:
apps/trigger/urls.py:27File:
apps/trigger/handler/impl/trigger/event_trigger.py:78The webhook endpoint is publicly accessible (no workspace_id in URL, no permission decorators).
Attack Chain
graph TD A[Attacker discovers trigger ID] --> B[POST /api/trigger/v1/webhook/trigger_id] B --> C[WebhookAuth.authenticate returns None, empty dict] C --> D[trigger_setting.get token returns None] D --> E[Token check skipped] E --> F{Trigger active?} F -->|Yes| G[EventTrigger.execute runs tasks] G --> H{Task type?} H -->|TOOL| I[ToolTask.execute called] I --> J[ToolExecutor.exec_code executes user Python code] J --> K[exec in subprocess - code execution] H -->|APPLICATION| L[ApplicationTask.execute runs workflow] L --> M[Workflow may contain tool nodes with exec] F -->|No| N[Returns 404 - no impact]Detailed Data Flow
POST /api/trigger/v1/webhook/{trigger_id}with arbitrary JSON bodyWebhookAuth.authenticate()→ returns(None, {})→ request is "authenticated"trigger_setting.get('token')→None→ skips token verificationget_parameters()processes the request body according to the trigger's body configurationsimple_tools.execute()iterates over active trigger tasks and dispatches themToolTask.execute()→ToolExecutor.exec_code()→exec()in a subprocessApplicationTask.execute()→ runs the application workflow, which may contain tool nodes that callexec()Key Enabling Factors
exec()is reachable from triggersImpact Assessment
Direct Impact
Unauthorized Trigger Invocation: Any unauthenticated user can invoke webhook triggers, causing them to execute their configured tasks (tool executions, application workflows).
Denial of Service: Repeated invocation of triggers with expensive operations (LLM calls, tool executions) can exhaust API credits, consume server resources, and degrade service availability.
Information Disclosure: If a trigger task involves an application workflow, the attacker can interact with the knowledge base and obtain AI-generated responses that may contain sensitive information.
Code Execution (conditional): If a trigger is bound to a custom Python tool, the attacker can trigger execution of that tool's code via
ToolExecutor.exec_code(). While this executes in a subprocess with optional sandbox restrictions, the code runs with the server's user privileges (sandbox is disabled by default —SANDBOXconfig defaults to0).Preconditions
is_active = True)trigger_settingmust lack atokenfield (possible via API-only creation or migration)Attack Scenarios
Scenario A: API-Only Trigger Creation (Most Likely)
tokenintrigger_settingScenario B: Trigger ID Leakage
Scenario C: Legacy/Migrated Triggers
Proof of Concept
See:
poc_maxkb_webhook_auth_bypass.pyUsage
Expected Vulnerable Response
Expected Protected Response (Token Present)
CVSS v3.1 Scoring
Base Score: 7.5 (High) —
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:NNote on Integrity: If the sandbox is disabled (default),
ToolExecutor.exec_code()runs user Python code with server-level privileges, enabling file manipulation, network access, and lateral movement within the infrastructure. Even with sandbox enabled, the tool can still perform actions within its permitted scope (e.g., LLM API calls, database operations via the application workflow).Adjusted Score (with sandbox disabled): 9.8 (Critical) — If code execution is reachable, this becomes a full RCE. However, we rate conservatively at 7.5 because: (a) trigger ID discovery is non-trivial, (b) the default frontend generates tokens, and (c) sandbox may be enabled in production deployments.
Fix Recommendations
1. Make Token Mandatory for EVENT Triggers (Critical)
File:
apps/trigger/serializers/trigger.py—_validate_event_setting()2. Migrate Existing Triggers Without Tokens
Add a data migration that generates tokens for all existing EVENT triggers that lack one:
3. Remove WebhookAuth or Make It Functional
Either remove
WebhookAuthentirely and rely solely on per-trigger token verification, or implement proper authentication:Important: In DRF, returning
Nonefromauthenticate()means "no authentication attempted" — the request proceeds withrequest.user = Noneandrequest.auth = None. The current code returns(None, {})which is incorrect — it signals successful authentication. This should be changed to returnNone(not a tuple).4. Add Rate Limiting
Add DRF throttle classes to the webhook endpoint:
5. Add Workspace Isolation (Defense in Depth)
Consider adding workspace validation to the webhook trigger execution to prevent cross-workspace trigger invocation:
Acknowledgments
CVE Request
This vulnerability is being submitted for CVE assignment through the relevant CNA. Key details for the request:
AI Cross-Review Notes
Verification Results
(None, {})which DRF interprets as successful auth. No middleware supplements this._validate_event_setting()does not require token. Model default is empty dict. Frontend auto-generates token but API does not enforce it.Key Mitigating Factor
The primary mitigating factor is trigger ID discovery. UUIDv7 provides ~122 bits of entropy, making blind enumeration infeasible. However:
trigger_taskAPI returns trigger IDs to authenticated usersDeveloper Likely Pushback Points
SANDBOX=0)Three-Question Filter
WebhookAuthreturns authenticated for all requests, and token verification is optional.Verdict: Confirmed vulnerability. Recommended for disclosure with severity High.