Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.250.224"
VERSION = "0.250.225"
IS_DEVELOPMENT = is_development_env_enabled()

SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax')
Expand Down
58 changes: 37 additions & 21 deletions application/single_app/functions_group_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
_normalize_bool,
_normalize_document_action_config,
_normalize_schedule,
_normalize_task_document_action_config,
_normalize_text,
_normalize_workflow_error_handling,
_normalize_workflow_tasks,
Expand All @@ -54,12 +55,9 @@
WORKFLOW_CONVERSATION_ACCESS_ERROR = 'Workflow conversation not found or access denied.'


def _normalize_group_document_action_config(group_id, workflow_data, existing_workflow=None, allow_empty_file_sync_targets=False):
action_config = _normalize_document_action_config(
workflow_data,
existing_workflow=existing_workflow,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,
)
def _apply_group_document_action_scope(group_id, action_config):
"""Force a normalized document action to stay inside the owning group workspace."""
action_config = action_config if isinstance(action_config, dict) else {'type': 'none'}
if action_config.get('type') == 'none':
return action_config

Expand All @@ -69,6 +67,15 @@
return action_config


def _normalize_group_document_action_config(group_id, workflow_data, existing_workflow=None, allow_empty_file_sync_targets=False):

Check warning on line 70 in application/single_app/functions_group_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
action_config = _normalize_document_action_config(
workflow_data,
existing_workflow=existing_workflow,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,

Check warning on line 74 in application/single_app/functions_group_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
)
return _apply_group_document_action_scope(group_id, action_config)


def _normalize_group_workflow_conversation_id(group_id, workflow_data, existing_workflow=None):
existing_workflow = existing_workflow if isinstance(existing_workflow, dict) else {}
conversation_id = _normalize_text(
Expand Down Expand Up @@ -440,6 +447,20 @@

workflow_name = _normalize_text(workflow_data.get('name'), 'Workflow name', required=True)
description = _normalize_text(workflow_data.get('description'), 'Description')
file_sync = _normalize_file_sync_config(
actor_user_id,
group_id,
workflow_data,
existing_workflow=existing_workflow,
user_info=user_info,
)
allow_empty_file_sync_targets = bool(file_sync.get('enabled') and file_sync.get('use_changed_documents'))

Check warning on line 457 in application/single_app/functions_group_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
document_action = _normalize_group_document_action_config(
group_id,
workflow_data,
existing_workflow=existing_workflow,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,

Check warning on line 462 in application/single_app/functions_group_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
)
tasks = _normalize_workflow_tasks(
workflow_data,
existing_workflow=existing_workflow,
Expand All @@ -450,6 +471,15 @@
settings=settings,
),
max_tasks=get_workflow_max_tasks(settings),
task_document_action_normalizer=lambda action_payload: _apply_group_document_action_scope(
group_id,
_normalize_task_document_action_config(
action_payload,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,

Check warning on line 478 in application/single_app/functions_group_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
settings=settings,
),
),
default_document_action=document_action,
)
task_prompt = _normalize_text(
workflow_data.get('task_prompt') or (tasks[0].get('instructions') if tasks else ''),
Expand Down Expand Up @@ -495,20 +525,6 @@
workflow_data.get('chat_capabilities_enabled', default_chat_capabilities_enabled),
default=default_chat_capabilities_enabled,
)
file_sync = _normalize_file_sync_config(
actor_user_id,
group_id,
workflow_data,
existing_workflow=existing_workflow,
user_info=user_info,
)
allow_empty_file_sync_targets = bool(file_sync.get('enabled') and file_sync.get('use_changed_documents'))
document_action = _normalize_group_document_action_config(
group_id,
workflow_data,
existing_workflow=existing_workflow,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,
)
if trigger_type == 'file_sync':
if not file_sync.get('enabled'):
raise ValueError('Monitor File Sync Changes workflows require File Sync before run.')
Expand Down Expand Up @@ -806,4 +822,4 @@
level=logging.WARNING,
)

return True
return True
70 changes: 61 additions & 9 deletions application/single_app/functions_personal_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@
existing_workflow=None,
task_runner_normalizer=None,
max_tasks=WORKFLOW_MAX_TASKS,
task_document_action_normalizer=None,
default_document_action=None,
):
workflow_data = workflow_data if isinstance(workflow_data, dict) else {}
existing_workflow = existing_workflow if isinstance(existing_workflow, dict) else {}
Expand Down Expand Up @@ -214,14 +216,27 @@
else:
raise ValueError(f'Workflow task {index + 1} runner could not be authorized.')

normalized_tasks.append({
normalized_task = {
'id': task_id,
'type': task_type,
'name': name,
'instructions': instructions,
'order': index + 1,
'runner': runner,
})
}

if callable(task_document_action_normalizer):
raw_document_action = raw_task.get('document_action')
if not isinstance(raw_document_action, dict) and index == 0:
# Workflows saved before per-task documents kept a single workflow-level
# action that only ever executed on the first task.
raw_document_action = default_document_action
try:

Check warning on line 234 in application/single_app/functions_personal_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
normalized_task['document_action'] = task_document_action_normalizer(raw_document_action)
except ValueError as exc:
raise ValueError(f'Workflow task {index + 1} ({name}): {exc}') from exc

normalized_tasks.append(normalized_task)

return normalized_tasks

Expand Down Expand Up @@ -286,6 +301,37 @@
)


def _normalize_task_document_action_config(action_payload, allow_empty_file_sync_targets=False, settings=None):

Check warning on line 304 in application/single_app/functions_personal_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
"""Normalize a single workflow task's document action payload."""
source_settings = settings if isinstance(settings, dict) else get_settings()

Check warning on line 306 in application/single_app/functions_personal_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
action_payload = action_payload if isinstance(action_payload, dict) else {'type': 'none'}
max_documents_by_type = get_document_action_max_documents_by_type(
DOCUMENT_ACTION_CONTEXT_WORKFLOW,
settings=source_settings,
)
allowed_action_types = get_enabled_document_action_types(settings=source_settings)

if allow_empty_file_sync_targets:

Check warning on line 314 in application/single_app/functions_personal_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
action_type = str(action_payload.get('type') or '').strip().lower()
document_ids = action_payload.get('document_ids') if isinstance(action_payload.get('document_ids'), list) else []
if action_type == DOCUMENT_ACTION_TYPE_ANALYZE and not document_ids:
placeholder_payload = dict(action_payload)
placeholder_payload['document_ids'] = ['__dynamic_file_sync_document__']
normalized_action = normalize_document_action_config(
action_payload=placeholder_payload,
max_documents_by_type=max_documents_by_type,
allowed_action_types=allowed_action_types,
)
normalized_action['document_ids'] = []
return normalized_action

return normalize_document_action_config(
action_payload=action_payload,
max_documents_by_type=max_documents_by_type,
allowed_action_types=allowed_action_types,
)


def _normalize_file_sync_config(user_id, workflow_data, existing_workflow=None):
workflow_data = workflow_data if isinstance(workflow_data, dict) else {}
existing_workflow = existing_workflow if isinstance(existing_workflow, dict) else {}
Expand Down Expand Up @@ -683,6 +729,13 @@

workflow_name = _normalize_text(workflow_data.get('name'), 'Workflow name', required=True)
description = _normalize_text(workflow_data.get('description'), 'Description')
file_sync = _normalize_file_sync_config(user_id, workflow_data, existing_workflow=existing_workflow)
allow_empty_file_sync_targets = bool(file_sync.get('enabled') and file_sync.get('use_changed_documents'))

Check warning on line 733 in application/single_app/functions_personal_workflows.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
document_action = _normalize_document_action_config(
workflow_data,
existing_workflow=existing_workflow,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,
)
tasks = _normalize_workflow_tasks(
workflow_data,
existing_workflow=existing_workflow,
Expand All @@ -692,6 +745,12 @@
settings=settings,
),
max_tasks=get_workflow_max_tasks(settings),
task_document_action_normalizer=lambda action_payload: _normalize_task_document_action_config(
action_payload,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,
settings=settings,
),
default_document_action=document_action,
)
task_prompt = _normalize_text(
workflow_data.get('task_prompt') or (tasks[0].get('instructions') if tasks else ''),
Expand Down Expand Up @@ -737,13 +796,6 @@
workflow_data.get('chat_capabilities_enabled', default_chat_capabilities_enabled),
default=default_chat_capabilities_enabled,
)
file_sync = _normalize_file_sync_config(user_id, workflow_data, existing_workflow=existing_workflow)
allow_empty_file_sync_targets = bool(file_sync.get('enabled') and file_sync.get('use_changed_documents'))
document_action = _normalize_document_action_config(
workflow_data,
existing_workflow=existing_workflow,
allow_empty_file_sync_targets=allow_empty_file_sync_targets,
)
if trigger_type == 'file_sync':
if not file_sync.get('enabled'):
raise ValueError('Monitor File Sync Changes workflows require File Sync before run.')
Expand Down
Loading
Loading